From bdcff7dbfe50309947d9642e4affe36441d87c56 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 15:49:29 +0100 Subject: [PATCH 01/77] Add OJDBC Log Analyzer MCP Server code --- src/ojdbc-log-analyzer-mcp-server/LICENSE.txt | 35 + src/ojdbc-log-analyzer-mcp-server/README.md | 54 ++ .../THIRD_PARTY_LICENSES.txt | 887 ++++++++++++++++++ src/ojdbc-log-analyzer-mcp-server/pom.xml | 109 +++ .../OracleJDBCLogAnalyzerMCPServer.java | 383 ++++++++ .../OracleJDBCLogAnalyzerMCPServerTest.java | 88 ++ 6 files changed, 1556 insertions(+) create mode 100644 src/ojdbc-log-analyzer-mcp-server/LICENSE.txt create mode 100644 src/ojdbc-log-analyzer-mcp-server/README.md create mode 100644 src/ojdbc-log-analyzer-mcp-server/THIRD_PARTY_LICENSES.txt create mode 100644 src/ojdbc-log-analyzer-mcp-server/pom.xml create mode 100644 src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServer.java create mode 100644 src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServerTest.java diff --git a/src/ojdbc-log-analyzer-mcp-server/LICENSE.txt b/src/ojdbc-log-analyzer-mcp-server/LICENSE.txt new file mode 100644 index 00000000..8dc7c070 --- /dev/null +++ b/src/ojdbc-log-analyzer-mcp-server/LICENSE.txt @@ -0,0 +1,35 @@ +Copyright (c) 2025 Oracle and/or its affiliates. + +The Universal Permissive License (UPL), Version 1.0 + +Subject to the condition set forth below, permission is hereby granted to any +person obtaining a copy of this software, associated documentation and/or data +(collectively the "Software"), free of charge and under any and all copyright +rights in the Software, and any and all patent rights owned or freely +licensable by each licensor hereunder covering either (i) the unmodified +Software as contributed to or provided by such licensor, or (ii) the Larger +Works (as defined below), to deal in both + +(a) the Software, and +(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +one is included with the Software (each a "Larger Work" to which the Software +is contributed by such licensors), + +without restriction, including without limitation the rights to copy, create +derivative works of, display, perform, and distribute the Software and make, +use, sell, offer for sale, import, export, have made, and have sold the +Software and the Larger Work(s), and to sublicense the foregoing rights on +either these or other terms. + +This license is subject to the following condition: +The above copyright notice and either this complete permission notice or at +a minimum a reference to the UPL must be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/ojdbc-log-analyzer-mcp-server/README.md b/src/ojdbc-log-analyzer-mcp-server/README.md new file mode 100644 index 00000000..fe825c8a --- /dev/null +++ b/src/ojdbc-log-analyzer-mcp-server/README.md @@ -0,0 +1,54 @@ +# Oracle JDBC Log Analyzer MCP Server + +## Overview + +The `ojdbc-log-analyzer-mcp-server` provides 8 tools for analyzing Oracle JDBC thin client logs and RDBMS/SQLNet trace files: + +### Oracle JDBC Log Analysis: + +- **`get-stats`**: Extracts performance statistics including error counts, sent/received packets and byte counts. +- **`get-queries`**: Retrieves all executed SQL queries with timestamps and execution times. +- **`get-errors`**: Extracts all errors reported by both server and client. +- **`get-connections-events`**: Shows connection open/close events. +- **`get-log-files-from-directory`**: Lists all Oracle JDBC log files in a directory. +- **`log-comparison`**: Compares two log files for performance metrics, errors, and network information. + +### RDBMS/SQLNet Trace Analysis: + +- **`get-rdbms-errors`**: Extracts errors from RDBMS/SQLNet trace files. +- **`get-packet-dumps`**: Extracts packet dumps for a specific connection ID. + +## Requirements + +Requirements to build the project: + +- JDK 17 (or higher) +- Maven 3.9 (or higher) + +## How to use + +### 1- Build the MCP server jar + +```bash +mvn clean install +``` + +The created jar can be found in `target/ojdbc-log-analyzer-mcp-server-1.0.0.jar`. + +### 2- Configuration + +Add (or merge) the following JSON to the configuration file to an MCP client (such as Claude or Cline): + +```json +{ + "mcpServers": { + "ojdbc-log-analyzer-mcp-server": { + "command": "java", + "args": [ + "-jar", + "/src/ojdbc-log-analyzer-mcp-server/target/ojdbc-log-analyzer-mcp-server-1.0.0.jar" + ] + } + } +} +``` diff --git a/src/ojdbc-log-analyzer-mcp-server/THIRD_PARTY_LICENSES.txt b/src/ojdbc-log-analyzer-mcp-server/THIRD_PARTY_LICENSES.txt new file mode 100644 index 00000000..fb4cc3e5 --- /dev/null +++ b/src/ojdbc-log-analyzer-mcp-server/THIRD_PARTY_LICENSES.txt @@ -0,0 +1,887 @@ +------------------------ Third-Party Components --------------------------------- +MCP Java SDK 0.10.0 + +*************************** +------------------------------- License and Notices for MCP Java SDK + + +Copyright: the original author or authors +=== Source URL: https://github.com/modelcontextprotocol/java-sdk/tree/v0.10.0/mcp +License: MIT + + ./LICENSE + +MIT License + +Copyright (c) 2025 the original author or authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------- Separator -------------- + +----------------------------------- Fourth-party dependencies ------------------------------------ + +Dependency: ch.randelshofer:fastdoubleparser +Copyright: Werner Randelshofer +=== Source URL: https://github.com/wrandelshofer/FastDoubleParser/tree/v1.0.0/fastdoubleparser +License: MIT + === https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE + +Copyright 2022 Tim Buktu + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------- Separator -------------- + + === https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL + +Copyright (c) Daniel Lemire + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +-------------- Separator -------------- + + === https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT + +MIT License + +Copyright (c) 2021 The fast_float authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +-------------- Separator -------------- + + ./LICENSE + +MIT License + +Copyright (c) 2023 Werner Randelshofer, Switzerland. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------- Separator -------------- + + === https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE + +MIT License + +Copyright (c) 2023 Werner Randelshofer, Switzerland. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------- Separator -------------- + + ./NOTICE + +# FastDoubleParser + +This is a Java port of Daniel Lemire's fast_float project. +This project provides parsers for double, float, BigDecimal and BigInteger values. + +## Copyright + +Copyright © 2023 Werner Randelshofer, Switzerland. + +## Licensing + +This code is licensed under MIT License. +https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE +(The file 'LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +Some portions of the code have been derived from other projects. +All these projects require that we include a copyright notice, and some require that we also include some text of their +license file. + +fast_double_parser, Copyright (c) 2022 Daniel Lemire. BSL License. +https://github.com/lemire/fast_double_parser +https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +fast_float, Copyright (c) 2021 The fast_float authors. MIT License. +https://github.com/fastfloat/fast_float +https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +bigint, Copyright 2020 Tim Buktu. 2-clause BSD License. +https://github.com/tbuktu/bigint/tree/floatfft +https://github.com/tbuktu/bigint/blob/617c8cd8a7c5e4fb4d919c6a4d11e2586107f029/LICENSE +https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE +(We only use those portions of the bigint project that can be licensed under 2-clause BSD License.) +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +-------------- Separator -------------- +Dependency: com.fasterxml.jackson.core:jackson-annotations +Copyright: FasterXML,LLC +=== Source URL: https://github.com/FasterXML/jackson-annotations/tree/jackson-annotations-2.17.0 +License: Apache 2.0 + ./LICENSE + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +-------------- Separator -------------- + + ./src/main/resources/META-INF/NOTICE + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Copyright + +Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) + +## Licensing + +Jackson 2.x core and extension components are licensed under Apache License 2.0 +To find the details that apply to this artifact see the accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS(-2.x) file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +-------------- Separator -------------- +Dependency: com.fasterxml.jackson.core:jackson-core +Copyright: FasterXML,LLC +=== Source URL: https://github.com/FasterXML/jackson-core/tree/jackson-core-2.17.0 +License: Apache 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./LICENSE + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./src/main/resources/META-INF/jackson-core-NOTICE + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Copyright + +Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) + +## Licensing + +Jackson 2.x core and extension components are licensed under Apache License 2.0 +To find the details that apply to this artifact see the accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS(-2.x) file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +## FastDoubleParser + +jackson-core bundles a shaded copy of FastDoubleParser . +That code is available under an MIT license +under the following copyright. + +Copyright © 2023 Werner Randelshofer, Switzerland. MIT License. + +See FastDoubleParser-NOTICE for details of other source code included in FastDoubleParser +and the licenses and copyrights that apply to that code. + +-------------- Separator -------------- +Dependency: com.fasterxml.jackson.core:jackson-databind +Copyright: FasterXML,LLC +=== Source URL: https://github.com/FasterXML/jackson-databind/tree/jackson-databind-2.17.0 +License: Apache 2.0 + ./LICENSE + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./src/main/resources/META-INF/NOTICE + +(Notice same as ./src/main/resources/META-INF/NOTICE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- +Dependency: io.projectreactor:reactor-core +Copyright: VMware Inc. +=== Source URL: https://github.com/reactor/reactor-core/tree/v3.7.0 +License: Apache 2.0 + ./codequality/spotless/licenseSlashstarStyle.txt + +/* + * Copyright (c) $YEAR VMware Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +-------------- Separator -------------- + + ./LICENSE + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- +Dependency: net.bytebuddy:byte-buddy +Copyright: Rafael Winterhalter +=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy +License: Apache 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./src/main/resources/META-INF/LICENSE + +This product bundles ASM ${version.asm}, which is available under a "3-clause BSD" +license. For details, see licenses/ASM. For more information visit ${asm.url}. + +-------------- Separator -------------- +Dependency: net.bytebuddy:byte-buddy-dep +Copyright: Rafael Winterhalter +=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy-dep +License: Apache 2.0 + ./LICENSE + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +-------------- Separator -------------- + + ./NOTICE + +Copyright ${project.inceptionYear} - Present ${copyright.holder} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +-------------- Separator -------------- +Dependency: org.ow2.asm:asm +Copyright: INRIA, France Telecom +=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm +License: BSD 3-Clause + ./LICENSE.txt + +BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) + +-------------- Separator -------------- +Dependency: org.ow2.asm:asm-commons +Copyright: INRIA, France Telecom +=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-commons +License: BSD 3-Clause + ./LICENSE.txt + + + ASM: a very small and fast Java bytecode manipulation framework + Copyright (c) 2000-2011 INRIA, France Telecom + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +-------------- Separator -------------- +Dependency: org.ow2.asm:asm-tree +Copyright: INRIA, France Telecom +=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-tree +License: BSD 3-Clause + ./LICENSE.txt + +BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) + +-------------- Separator -------------- +Dependency: org.reactivestreams:reactive-streams +Copyright: Reactive Streams +=== Source URL: https://github.com/reactive-streams/reactive-streams-jvm/tree/v1.0.4 +License: MIT + ./LICENSE + +MIT No Attribution + +Copyright 2014 Reactive Streams + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------- Separator -------------- +Dependency: org.slf4j:slf4j-api +Copyright: QOS.ch +=== Source URL: https://github.com/qos-ch/slf4j/tree/v_2.0.16/slf4j-api +License: MIT + ./LICENSE.txt + +Copyright (c) 2004-2023 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +-------------- Separator -------------- + + + +=== Entry created by OSCS on 2025-07-03 09:39:14 + diff --git a/src/ojdbc-log-analyzer-mcp-server/pom.xml b/src/ojdbc-log-analyzer-mcp-server/pom.xml new file mode 100644 index 00000000..173a832b --- /dev/null +++ b/src/ojdbc-log-analyzer-mcp-server/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + + com.oracle.database.jdbc + ojdbc-log-analyzer-mcp-server + 1.0.0 + jar + Oracle JDBC Log Analyzer MCP Server + The OJDBC Log Analyzer MCP Server provides tools for analyzing + Oracle JDBC thin client logs and RDBMS/SQLNet trace files. + https://github.com/Youssef-Erradi/mcp/tree/main/src/ojdbc-log-analyzer-mcp-server + + + + The Universal Permissive License (UPL), Version 1.0 + https://oss.oracle.com/licenses/upl/ + + + + + + Oracle Corporation + https://www.oracle.com + + + + + 17 + 0.12.1 + 23.9.0.25.07 + 1.0.0 + 12.1.4 + 5.10.0 + ${java.version} + ${java.version} + UTF-8 + UTF-8 + + + + + io.modelcontextprotocol.sdk + mcp + ${mcp.version} + + + + com.oracle.database.jdbc + ojdbc17 + ${ojdbc.version} + + + + com.oracle.database.jdbc + ojdbc-log-analyzer + ${logAnalyzer.version} + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + + + com.oracle.database.jdbc.mcp.loganalyzer.OracleJDBCLogAnalyzerMCPServer + + + + + + + + + + + ${project.basedir} + + THIRD_PARTY_LICENSES.txt + + META-INF + + + + ${project.basedir} + + LICENSE.txt + + META-INF + + + + + + \ No newline at end of file diff --git a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServer.java b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServer.java new file mode 100644 index 00000000..5d086ed4 --- /dev/null +++ b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServer.java @@ -0,0 +1,383 @@ +/* + ** Oracle JDBC Log Analyzer MCP Server version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + +package com.oracle.database.jdbc.mcp.loganalyzer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; +import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.spec.McpSchema.Tool; +import io.modelcontextprotocol.spec.McpSchema.CallToolResult; +import io.modelcontextprotocol.spec.McpSchema.TextContent; +import oracle.jdbc.logs.analyzer.JDBCLog; +import oracle.jdbc.logs.analyzer.RDBMSLog; +import oracle.jdbc.logs.model.JDBCConnectionEvent; +import oracle.jdbc.logs.model.JDBCExecutedQuery; +import oracle.jdbc.logs.model.LogError; +import oracle.jdbc.logs.model.RDBMSError; +import oracle.jdbc.logs.model.RDBMSPacketDump; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * This class provides an MCP Server for Oracle JDBC Log Analyzer with tools + * to analyze and process Oracle JDBC and RDBMS/SQLNet log files. + *

+ */ +public final class OracleJDBCLogAnalyzerMCPServer { + + public static void main(String[] args) { + McpServer.sync(new StdioServerTransportProvider(new ObjectMapper())) + .serverInfo("ojdbc-log-analyzer-mcp-server", "1.0.0") + .capabilities(McpSchema.ServerCapabilities.builder() + .tools(true) + .logging() + .build()) + .tools(getSyncToolSpecifications()) + .build(); + } + + private static final String FILE_PATH = "filePath"; + private static final String SECOND_FILE_PATH = "secondFilePath"; + private static final String CONNECTION_ID = "connectionId"; + private static final String FILE_PATH_SCHEMA = """ + { + "type": "object", + "properties": { + "filePath": { + "type": "string", + "description": "Absolute path or an URL to the Oracle JDBC log file." + } + }, + "required": ["filePath"] + } + """; + private static final String FILE_COMPARISON_SCHEMA = """ + { + "type": "object", + "properties": { + "filePath": { + "type": "string", + "description": "Absolute path or an URL to the 1st Oracle JDBC log file" + }, + "secondFilePath": { + "type": "string", + "description": "Absolute path or an URL to the 2nd Oracle JDBC log file" + } + }, + "required": ["filePath", "secondFilePath"] + } + """; + private static final String RDBMS_TOOLS_SCHEMA = """ + { + "type": "object", + "properties": { + "filePath": { + "type": "string", + "description": "Absolute path or an URL to the RDBMS/SQLNet trace file" + }, + "connectionId": { + "type": "string", + "description": "Connection ID string" + } + }, + "required": ["filePath", "connectionId"] + } + """; + + private OracleJDBCLogAnalyzerMCPServer() { + // Prevent instantiation + } + + /** + *

+ * Returns the list of all available SyncToolSpecification instances for this server. + *

+ */ + public static List getSyncToolSpecifications() { + return List.of( + buildGetStatsTool(), + buildGetQueriesTool(), + buildGetErrorsTool(), + buildListLogsDirectoryTool(), + buildGetConnectionEventsTool(), + buildLogComparisonTool(), + buildGetRdbmsErrorsTool(), + buildGetPacketDumpsTool()); + } + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} + * for the {@code get-stats} tool, which retrieves high-level statistics from an Oracle JDBC thin log file. + * The tool gathers information such as error count, the number of sent and + * received packets, and byte counts from the specified log file. + *

+ * + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-stats} tool. + */ + private static SyncToolSpecification buildGetStatsTool() { + return SyncToolSpecification.builder() + .tool(McpSchema.Tool.builder() + .name("get-stats") + .title("Get JDBC Log Stats") + .description("Return aggregated stats (error count, packets, bytes) from an Oracle JDBC thin log.") + .inputSchema(FILE_PATH_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall( () -> { + final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var stats = new JDBCLog(filePath).getStats(); + return stats.toJSONString(); + })) + .build(); + } + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-queries} tool. + * This tool extracts all executed SQL queries from an Oracle JDBC thin log file. + * For each query, it provides the corresponding timestamp, execution time, connection id and tenant. + *

+ * + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-queries} tool. + */ + private static SyncToolSpecification buildGetQueriesTool() { + return SyncToolSpecification.builder() + .tool(Tool.builder() + .name("get-queries") + .description("Get all executed queries from an Oracle JDBC thin log file, including the timestamp and execution time.") + .inputSchema(FILE_PATH_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var queries = new JDBCLog(filePath).getQueries(); + return queries.stream() + .map(JDBCExecutedQuery::toJSONString) + .collect(Collectors.joining(",", "[", "]")); + })) + .build(); + } + + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the get-errors tool. + * This tool processes a specified Oracle JDBC thin log file and extracts all reported errors. + * Each error record includes details such as the stack trace and additional log context. + *

+ * ` + * @return a {@link SyncToolSpecification SyncToolSpecification} representing the get-errors tool. + */ + private static SyncToolSpecification buildGetErrorsTool() { + return SyncToolSpecification.builder() + .tool(Tool.builder() + .name("get-errors") + .description("Get all reported errors from an Oracle JDBC thin log file, including stacktrace and log context.") + .inputSchema(FILE_PATH_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var errors = new JDBCLog(filePath).getLogErrors(); + return errors.stream() + .map(LogError::toJSONString) + .collect(Collectors.joining(",", "[", "]")); + })) + .build(); + } + + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-log-files-from-directory} tool. + * This tool lists all Oracle JDBC log files present in the specified directory path. + *

+ * + * @return a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-log-files-from-directory} tool. + */ + private static SyncToolSpecification buildListLogsDirectoryTool() { + return SyncToolSpecification.builder() + .tool(Tool.builder() + .name("get-log-files-from-directory") + .description("List all Oracle JDBC log files from the specified directory.") + .inputSchema(FILE_PATH_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + final var directoryPath = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var directory = new File(directoryPath); + final var files = directory.listFiles(); + if (files == null || files.length == 0) { + throw new IOException("No files found in the specified directory."); + } + return Arrays.stream(files) + .filter(file -> !file.isHidden() && file.isFile()) + .map(File::getName) + .collect(Collectors.joining(",", "[", "]")); + })) + .build(); + } + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code log-comparison} tool. + * This tool enables comparison of two Oracle JDBC log files. It analyzes the specified log files and provides a JSON report + * highlighting differences and similarities in performance metrics, encountered errors, and network-related information. + *

+ * + * @return a {@link SyncToolSpecification SyncToolSpecification} instance that defines the {@code log-comparison} tool. + */ + private static SyncToolSpecification buildLogComparisonTool() { + return SyncToolSpecification.builder() + .tool(Tool.builder() + .name("log-comparison") + .description("Compare two JDBC log files for performance metrics, errors, and network information.") + .inputSchema(FILE_COMPARISON_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var secondFilePath = String.valueOf(callReq.arguments().get(SECOND_FILE_PATH)); + final var comparison = new JDBCLog(filePath).compareTo(secondFilePath); + return comparison.toJSONString(); + })) + .build(); + } + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-connection-events} tool. + *

+ * + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-connection-events} tool. + */ + private static SyncToolSpecification buildGetConnectionEventsTool() { + return SyncToolSpecification.builder() + .tool(Tool.builder() + .name("get-connection-events") + .description("Retrieve opened and closed JDBC connection events from the log file with timestamp and connection details.") + .inputSchema(FILE_PATH_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var events = new JDBCLog(filePath).getConnectionEvents(); + return events.stream() + .map(JDBCConnectionEvent::toJSONString) + .collect(Collectors.joining(",", "[", "]")); + })).build(); + } + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the get-rdbms-errors tool. + * This tool processes a specified Oracle RDBMS/SQLNet trace file to extract all reported errors. + * Each extracted error record includes relevant details, such as error messages and context information, + * and is serialized in JSON format. + *

+ * + * @return a {@link SyncToolSpecification SyncToolSpecification} representing the {@code get-rdbms-errors} tool. + */ + private static SyncToolSpecification buildGetRdbmsErrorsTool() { + return SyncToolSpecification.builder() + .tool(Tool.builder() + .name("get-rdbms-errors") + .description("Retrieve errors from an RDBMS/SQLNet trace file.") + .inputSchema(RDBMS_TOOLS_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + final var logFile = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var errors = new RDBMSLog(logFile).getErrors(); + return errors.stream() + .map(RDBMSError::toJSONString) + .collect(Collectors.joining(",", "[", "]")); + })) + .build(); + } + + /** + *

+ * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the get-packet-dumps tool. + * This tool extracts packet dump information from a specified RDBMS/SQLNet trace file that matches a given connection ID. + * Each packet dump record includes its associated details and is serialized in JSON format. + *

+ * + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-packet-dumps} tool. + */ + private static SyncToolSpecification buildGetPacketDumpsTool() { + return SyncToolSpecification.builder() + .tool(Tool.builder() + .name("get-packet-dumps") + .description("Extract packet dumps from RDBMS/SQLNet trace file for given connection ID.") + .inputSchema(RDBMS_TOOLS_SCHEMA) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); + final var connId = String.valueOf(callReq.arguments().get(CONNECTION_ID)); + final var packetDumps = new RDBMSLog(filePath).getPacketDumps(connId); + return packetDumps.stream() + .map(RDBMSPacketDump::toJSONString) + .collect(Collectors.joining(",", "[", "]")); + })) + .build(); + } + + /** + *

+ * A functional interface similar to {@link java.util.function.Supplier Supplier}, but allows for throwing an {@link IOException}. + *

+ * + * @param the type of results supplied by this supplier + */ + @FunctionalInterface + private interface ThrowingSupplier { + /** + * Gets a result, potentially throwing an {@link IOException}. + * + * @return a result + * @throws IOException if an I/O error occurs + */ + T get() throws IOException; + } + + + /** + *

+ * Executes the given {@link ThrowingSupplier ThrowingSupplier} action that may throw an {@link IOException} + * and wraps the result in a {@link CallToolResult CallToolResult}. If the action completes successfully, + * the result is returned in a {@link CallToolResult CallToolResult} with {@code isError} set to {@code false}. + * If an {@link IOException} is thrown, the exception message is returned as {@link TextContent TextContent} + * and {@code isError} is set to {@code true}. + *

+ * + *

+ * This utility method is used to standardize error handling and result formatting for + * methods that perform I/O operations and return responses to the MCP server. + *

+ * + * @param action The supplier action to execute, which may throw an {@link IOException}. + * @return a {@link CallToolResult} containing the response and error flag indicating success or failure. + */ + private static CallToolResult tryCall(ThrowingSupplier action) { + boolean isError = false; + String response; + try { + response = action.get(); + } catch (IOException e) { + response = e.getMessage(); + isError = true; + } + + return CallToolResult.builder() + .addTextContent(response) + .isError(isError) + .build(); + } +} diff --git a/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServerTest.java b/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServerTest.java new file mode 100644 index 00000000..fec58c74 --- /dev/null +++ b/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServerTest.java @@ -0,0 +1,88 @@ +package com.oracle.database.jdbc.mcp.loganalyzer; + +import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; +import io.modelcontextprotocol.spec.McpSchema; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Map; +import java.util.stream.Collectors; + +import static java.util.function.Function.identity; +import static org.junit.jupiter.api.Assertions.*; + +@SuppressWarnings("unchecked") +class OracleJDBCLogAnalyzerMCPServerTest { + + private static Map tools; + + @BeforeAll + static void initializeTools(){ + tools = OracleJDBCLogAnalyzerMCPServer.getSyncToolSpecifications() + .stream() + .map(SyncToolSpecification::tool) + .collect(Collectors.toMap(McpSchema.Tool::name, identity())); + } + + @ParameterizedTest + @ValueSource(strings = { + "get-stats", + "get-queries", + "get-errors", + "get-log-files-from-directory", + "get-connection-events", + "log-comparison", + "get-rdbms-errors", + "get-packet-dumps" + }) + void testToolPresence(final String toolName) { + final var isToolPresent = tools.containsKey(toolName); + assertTrue(isToolPresent, toolName + " tool should be present."); + } + + @Test + void testFilePathParameterInAllTools() { + for (var tool : tools.values()) { + final var properties = tool.inputSchema().properties(); + assertTrue(properties.containsKey("filePath"), "Every tool " + + "should have filePath parameter"); + + final var filePathProperty = (Map) properties.get("filePath"); + assertEquals("string", filePathProperty.get("type")); + } + } + + @Test + void testSecondFilePathParameterInLogComparisonTool() { + final var toolProperties = tools.get("log-comparison") + .inputSchema() + .properties(); + + final var isSecondFileParameterPresent = toolProperties.containsKey("filePath"); + assertTrue(isSecondFileParameterPresent, "log-comparison tool " + + "should have 'secondFilePath' parameter."); + + final var secondFilePathProperty = (Map) toolProperties.get("secondFilePath"); + + assertEquals("string", secondFilePathProperty.get("type"), + "The type of 'filePath' and 'secondFilePath' parameters should be 'string'"); + } + + @Test + void testConnectionIdParameterInGetPacketDumpsTool() { + final var toolProperties = tools.get("get-packet-dumps") + .inputSchema() + .properties(); + + final var isConnectionIdPresent = toolProperties.containsKey("connectionId"); + assertTrue(isConnectionIdPresent, "log-comparison tool " + + "should have 'connectionId' parameter."); + + var connectionIdProperty = (Map) toolProperties.get("connectionId"); + + assertEquals("string", connectionIdProperty.get("type"), + "The type of 'filePath' and 'secondFilePath' parameters should be 'string'"); + } +} \ No newline at end of file From c470afd12422fe02acf829ca15c3639bb02d3022 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 15:50:14 +0100 Subject: [PATCH 02/77] Add target and dependency-reduced-pom.xml to .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 3258903c..b8e3cd0d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ venv.bak/ .coverage htmlcov/ + +# Maven +**/target/ +dependency-reduced-pom.xml \ No newline at end of file From a4299fe7e83958fcafa740faccfa06659c813bd3 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 18:15:11 +0100 Subject: [PATCH 03/77] Fix package name --- .../{mcp/loganalyzer => }/OracleJDBCLogAnalyzerMCPServer.java | 2 +- .../loganalyzer => }/OracleJDBCLogAnalyzerMCPServerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/{mcp/loganalyzer => }/OracleJDBCLogAnalyzerMCPServer.java (99%) rename src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/{mcp/loganalyzer => }/OracleJDBCLogAnalyzerMCPServerTest.java (98%) diff --git a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServer.java b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java similarity index 99% rename from src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServer.java rename to src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java index 5d086ed4..916424db 100644 --- a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServer.java +++ b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java @@ -5,7 +5,7 @@ ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ */ -package com.oracle.database.jdbc.mcp.loganalyzer; +package com.oracle.database.jdbc; import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.server.McpServer; diff --git a/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServerTest.java b/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java similarity index 98% rename from src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServerTest.java rename to src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java index fec58c74..cb757d23 100644 --- a/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/mcp/loganalyzer/OracleJDBCLogAnalyzerMCPServerTest.java +++ b/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java @@ -1,4 +1,4 @@ -package com.oracle.database.jdbc.mcp.loganalyzer; +package com.oracle.database.jdbc; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.spec.McpSchema; From 4ccc4a09ebcd22e7282aa050d426ef234b623c73 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 18:15:51 +0100 Subject: [PATCH 04/77] Fix configuration for maven shade plugin --- src/ojdbc-log-analyzer-mcp-server/pom.xml | 29 +++++++---------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/ojdbc-log-analyzer-mcp-server/pom.xml b/src/ojdbc-log-analyzer-mcp-server/pom.xml index 173a832b..7cedb7cb 100644 --- a/src/ojdbc-log-analyzer-mcp-server/pom.xml +++ b/src/ojdbc-log-analyzer-mcp-server/pom.xml @@ -9,7 +9,7 @@ 1.0.0 jar Oracle JDBC Log Analyzer MCP Server - The OJDBC Log Analyzer MCP Server provides tools for analyzing + The Oracle JDBC Log Analyzer MCP Server provides tools for analyzing Oracle JDBC thin client logs and RDBMS/SQLNet trace files. https://github.com/Youssef-Erradi/mcp/tree/main/src/ojdbc-log-analyzer-mcp-server @@ -32,7 +32,6 @@ 0.12.1 23.9.0.25.07 1.0.0 - 12.1.4 5.10.0 ${java.version} ${java.version} @@ -72,12 +71,18 @@ org.apache.maven.plugins maven-shade-plugin + 3.6.1 + package + + shade + + false - com.oracle.database.jdbc.mcp.loganalyzer.OracleJDBCLogAnalyzerMCPServer + com.oracle.database.jdbc.OracleJDBCLogAnalyzerMCPServer @@ -86,24 +91,6 @@ - - - ${project.basedir} - - THIRD_PARTY_LICENSES.txt - - META-INF - - - - ${project.basedir} - - LICENSE.txt - - META-INF - - - \ No newline at end of file From 4c9408f4c92f7ee2b4e810ae9a7e6d206e270996 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 18:17:07 +0100 Subject: [PATCH 05/77] Remove dependency-reduced-pom.xml from .gitignore file --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index b8e3cd0d..0a9e261d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,3 @@ htmlcov/ # Maven **/target/ -dependency-reduced-pom.xml \ No newline at end of file From 9e6d90beb240a706385860294ebf93e0f7b1b6db Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 18:22:47 +0100 Subject: [PATCH 06/77] Add type to the provided JSON configuration --- src/ojdbc-log-analyzer-mcp-server/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ojdbc-log-analyzer-mcp-server/README.md b/src/ojdbc-log-analyzer-mcp-server/README.md index fe825c8a..617b200b 100644 --- a/src/ojdbc-log-analyzer-mcp-server/README.md +++ b/src/ojdbc-log-analyzer-mcp-server/README.md @@ -43,6 +43,7 @@ Add (or merge) the following JSON to the configuration file to an MCP client (su { "mcpServers": { "ojdbc-log-analyzer-mcp-server": { + "type": "stdio", "command": "java", "args": [ "-jar", From ceca78cb048e4ec6c38ecce9cf35221e401878b0 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 18:32:41 +0100 Subject: [PATCH 07/77] Add title to tools and fix their names --- .../jdbc/OracleJDBCLogAnalyzerMCPServer.java | 60 +++++++++++-------- .../OracleJDBCLogAnalyzerMCPServerTest.java | 2 +- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java index 916424db..1fe93395 100644 --- a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java +++ b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java @@ -107,14 +107,14 @@ private OracleJDBCLogAnalyzerMCPServer() { */ public static List getSyncToolSpecifications() { return List.of( - buildGetStatsTool(), - buildGetQueriesTool(), - buildGetErrorsTool(), - buildListLogsDirectoryTool(), - buildGetConnectionEventsTool(), - buildLogComparisonTool(), - buildGetRdbmsErrorsTool(), - buildGetPacketDumpsTool()); + getStatsTool(), + getQueriesTool(), + getErrorsTool(), + getListLogsDirectoryTool(), + getConnectionEventsTool(), + logComparisonTool(), + getRdbmsErrorsTool(), + getPacketDumpsTool()); } /** @@ -127,11 +127,11 @@ public static List getSyncToolSpecifications() { * * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-stats} tool. */ - private static SyncToolSpecification buildGetStatsTool() { + private static SyncToolSpecification getStatsTool() { return SyncToolSpecification.builder() .tool(McpSchema.Tool.builder() .name("get-stats") - .title("Get JDBC Log Stats") + .title("Get JDBC Stats") .description("Return aggregated stats (error count, packets, bytes) from an Oracle JDBC thin log.") .inputSchema(FILE_PATH_SCHEMA) .build()) @@ -152,13 +152,14 @@ private static SyncToolSpecification buildGetStatsTool() { * * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-queries} tool. */ - private static SyncToolSpecification buildGetQueriesTool() { + private static SyncToolSpecification getQueriesTool() { return SyncToolSpecification.builder() .tool(Tool.builder() - .name("get-queries") - .description("Get all executed queries from an Oracle JDBC thin log file, including the timestamp and execution time.") - .inputSchema(FILE_PATH_SCHEMA) - .build()) + .name("get-queries") + .title("Get JDBC Queries") + .description("Get all executed queries from an Oracle JDBC thin log file, including the timestamp and execution time.") + .inputSchema(FILE_PATH_SCHEMA) + .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var queries = new JDBCLog(filePath).getQueries(); @@ -179,15 +180,16 @@ private static SyncToolSpecification buildGetQueriesTool() { * ` * @return a {@link SyncToolSpecification SyncToolSpecification} representing the get-errors tool. */ - private static SyncToolSpecification buildGetErrorsTool() { + private static SyncToolSpecification getErrorsTool() { return SyncToolSpecification.builder() .tool(Tool.builder() .name("get-errors") + .title("Get JDBC Errors") .description("Get all reported errors from an Oracle JDBC thin log file, including stacktrace and log context.") .inputSchema(FILE_PATH_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { - final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); + .callHandler((exchange, callReq) -> tryCall(() -> { + final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var errors = new JDBCLog(filePath).getLogErrors(); return errors.stream() .map(LogError::toJSONString) @@ -205,11 +207,12 @@ private static SyncToolSpecification buildGetErrorsTool() { * * @return a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-log-files-from-directory} tool. */ - private static SyncToolSpecification buildListLogsDirectoryTool() { + private static SyncToolSpecification getListLogsDirectoryTool() { return SyncToolSpecification.builder() .tool(Tool.builder() - .name("get-log-files-from-directory") - .description("List all Oracle JDBC log files from the specified directory.") + .name("list-log-files-from-directory") + .title("List Files From Directory") + .description("List visible files from a specified directory, which helps the user to analyze multiple files with one prompt.") .inputSchema(FILE_PATH_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { @@ -236,10 +239,11 @@ private static SyncToolSpecification buildListLogsDirectoryTool() { * * @return a {@link SyncToolSpecification SyncToolSpecification} instance that defines the {@code log-comparison} tool. */ - private static SyncToolSpecification buildLogComparisonTool() { + private static SyncToolSpecification logComparisonTool() { return SyncToolSpecification.builder() .tool(Tool.builder() .name("log-comparison") + .title("JDBC Log Comparison") .description("Compare two JDBC log files for performance metrics, errors, and network information.") .inputSchema(FILE_COMPARISON_SCHEMA) .build()) @@ -259,10 +263,11 @@ private static SyncToolSpecification buildLogComparisonTool() { * * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-connection-events} tool. */ - private static SyncToolSpecification buildGetConnectionEventsTool() { + private static SyncToolSpecification getConnectionEventsTool() { return SyncToolSpecification.builder() .tool(Tool.builder() .name("get-connection-events") + .title("Get JDBC Connection Events") .description("Retrieve opened and closed JDBC connection events from the log file with timestamp and connection details.") .inputSchema(FILE_PATH_SCHEMA) .build()) @@ -272,7 +277,8 @@ private static SyncToolSpecification buildGetConnectionEventsTool() { return events.stream() .map(JDBCConnectionEvent::toJSONString) .collect(Collectors.joining(",", "[", "]")); - })).build(); + })) + .build(); } /** @@ -285,10 +291,11 @@ private static SyncToolSpecification buildGetConnectionEventsTool() { * * @return a {@link SyncToolSpecification SyncToolSpecification} representing the {@code get-rdbms-errors} tool. */ - private static SyncToolSpecification buildGetRdbmsErrorsTool() { + private static SyncToolSpecification getRdbmsErrorsTool() { return SyncToolSpecification.builder() .tool(Tool.builder() .name("get-rdbms-errors") + .title("Get RDBMS/SQLNet Errors") .description("Retrieve errors from an RDBMS/SQLNet trace file.") .inputSchema(RDBMS_TOOLS_SCHEMA) .build()) @@ -311,10 +318,11 @@ private static SyncToolSpecification buildGetRdbmsErrorsTool() { * * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-packet-dumps} tool. */ - private static SyncToolSpecification buildGetPacketDumpsTool() { + private static SyncToolSpecification getPacketDumpsTool() { return SyncToolSpecification.builder() .tool(Tool.builder() .name("get-packet-dumps") + .title("Get RDBMS/SQLNet Packet Dumps") .description("Extract packet dumps from RDBMS/SQLNet trace file for given connection ID.") .inputSchema(RDBMS_TOOLS_SCHEMA) .build()) diff --git a/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java b/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java index cb757d23..223b764a 100644 --- a/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java +++ b/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java @@ -31,7 +31,7 @@ static void initializeTools(){ "get-stats", "get-queries", "get-errors", - "get-log-files-from-directory", + "list-log-files-from-directory", "get-connection-events", "log-comparison", "get-rdbms-errors", From ba21492435e5047350cea2c99d16845be93b19af Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 19 Nov 2025 18:38:25 +0100 Subject: [PATCH 08/77] Fix name and description of list-log-files-from-directory tool --- src/ojdbc-log-analyzer-mcp-server/README.md | 2 +- .../oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ojdbc-log-analyzer-mcp-server/README.md b/src/ojdbc-log-analyzer-mcp-server/README.md index 617b200b..0da306df 100644 --- a/src/ojdbc-log-analyzer-mcp-server/README.md +++ b/src/ojdbc-log-analyzer-mcp-server/README.md @@ -10,7 +10,7 @@ The `ojdbc-log-analyzer-mcp-server` provides 8 tools for analyzing Oracle JDBC t - **`get-queries`**: Retrieves all executed SQL queries with timestamps and execution times. - **`get-errors`**: Extracts all errors reported by both server and client. - **`get-connections-events`**: Shows connection open/close events. -- **`get-log-files-from-directory`**: Lists all Oracle JDBC log files in a directory. +- **`list-log-files-from-directory`**: List all visible files from a specified directory, which helps the user analyze multiple files with one prompt. - **`log-comparison`**: Compares two log files for performance metrics, errors, and network information. ### RDBMS/SQLNet Trace Analysis: diff --git a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java index 1fe93395..78bec58f 100644 --- a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java +++ b/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java @@ -212,7 +212,7 @@ private static SyncToolSpecification getListLogsDirectoryTool() { .tool(Tool.builder() .name("list-log-files-from-directory") .title("List Files From Directory") - .description("List visible files from a specified directory, which helps the user to analyze multiple files with one prompt.") + .description("List all visible files from a specified directory, which helps the user analyze multiple files with one prompt.") .inputSchema(FILE_PATH_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { From f1f196951e6e4352be4a3f0637666505022b182f Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 20 Nov 2025 15:48:32 +0100 Subject: [PATCH 09/77] Adding support for yaml config (custom tools and database connections) --- .../LICENSE.txt | 0 .../README.md | 22 + .../THIRD_PARTY_LICENSES.txt | 1774 ++++++++--------- .../pom.xml | 11 +- .../jdbc/OracleDBToolboxMCPServer.java | 158 ++ .../jdbc/OracleJDBCLogAnalyzerMCPServer.java | 114 +- .../oracle/database/jdbc/ServerConfig.java | 172 ++ .../database/jdbc/config/ConfigRoot.java | 8 + .../database/jdbc/config/SourceConfig.java | 18 + .../database/jdbc/config/ToolConfig.java | 11 + .../jdbc/config/ToolParameterConfig.java | 7 + .../OracleJDBCLogAnalyzerMCPServerTest.java | 2 +- 12 files changed, 1319 insertions(+), 978 deletions(-) rename src/{ojdbc-log-analyzer-mcp-server => oracle-db-toolbox-mcp-server}/LICENSE.txt (100%) rename src/{ojdbc-log-analyzer-mcp-server => oracle-db-toolbox-mcp-server}/README.md (75%) rename src/{ojdbc-log-analyzer-mcp-server => oracle-db-toolbox-mcp-server}/THIRD_PARTY_LICENSES.txt (97%) rename src/{ojdbc-log-analyzer-mcp-server => oracle-db-toolbox-mcp-server}/pom.xml (91%) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java rename src/{ojdbc-log-analyzer-mcp-server => oracle-db-toolbox-mcp-server}/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java (80%) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java rename src/{ojdbc-log-analyzer-mcp-server => oracle-db-toolbox-mcp-server}/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java (97%) diff --git a/src/ojdbc-log-analyzer-mcp-server/LICENSE.txt b/src/oracle-db-toolbox-mcp-server/LICENSE.txt similarity index 100% rename from src/ojdbc-log-analyzer-mcp-server/LICENSE.txt rename to src/oracle-db-toolbox-mcp-server/LICENSE.txt diff --git a/src/ojdbc-log-analyzer-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md similarity index 75% rename from src/ojdbc-log-analyzer-mcp-server/README.md rename to src/oracle-db-toolbox-mcp-server/README.md index 0da306df..887509d4 100644 --- a/src/ojdbc-log-analyzer-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -53,3 +53,25 @@ Add (or merge) the following JSON to the configuration file to an MCP client (su } } ``` + +## YAML Configuration Support + +The MCP server supports loading database connection and tool definitions from a YAML configuration file. + +**Example `config.yaml`:** +```yaml +sources: + prod-db: + url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 + user: ADMIN + password: mypassword + +tools: + hotels-by-name: + source: prod-db + statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' +``` +To enable YAML configuration, launch the server with: +```bash +java -DconfigFile=/path/to/config.yaml -jar .jar +``` \ No newline at end of file diff --git a/src/ojdbc-log-analyzer-mcp-server/THIRD_PARTY_LICENSES.txt b/src/oracle-db-toolbox-mcp-server/THIRD_PARTY_LICENSES.txt similarity index 97% rename from src/ojdbc-log-analyzer-mcp-server/THIRD_PARTY_LICENSES.txt rename to src/oracle-db-toolbox-mcp-server/THIRD_PARTY_LICENSES.txt index fb4cc3e5..698dc944 100644 --- a/src/ojdbc-log-analyzer-mcp-server/THIRD_PARTY_LICENSES.txt +++ b/src/oracle-db-toolbox-mcp-server/THIRD_PARTY_LICENSES.txt @@ -1,887 +1,887 @@ ------------------------- Third-Party Components --------------------------------- -MCP Java SDK 0.10.0 - -*************************** -------------------------------- License and Notices for MCP Java SDK - - -Copyright: the original author or authors -=== Source URL: https://github.com/modelcontextprotocol/java-sdk/tree/v0.10.0/mcp -License: MIT - - ./LICENSE - -MIT License - -Copyright (c) 2025 the original author or authors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------- Separator -------------- - ------------------------------------ Fourth-party dependencies ------------------------------------ - -Dependency: ch.randelshofer:fastdoubleparser -Copyright: Werner Randelshofer -=== Source URL: https://github.com/wrandelshofer/FastDoubleParser/tree/v1.0.0/fastdoubleparser -License: MIT - === https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE - -Copyright 2022 Tim Buktu - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------- Separator -------------- - - === https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL - -Copyright (c) Daniel Lemire - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. --------------- Separator -------------- - - === https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT - -MIT License - -Copyright (c) 2021 The fast_float authors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - --------------- Separator -------------- - - ./LICENSE - -MIT License - -Copyright (c) 2023 Werner Randelshofer, Switzerland. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------- Separator -------------- - - === https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE - -MIT License - -Copyright (c) 2023 Werner Randelshofer, Switzerland. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------- Separator -------------- - - ./NOTICE - -# FastDoubleParser - -This is a Java port of Daniel Lemire's fast_float project. -This project provides parsers for double, float, BigDecimal and BigInteger values. - -## Copyright - -Copyright © 2023 Werner Randelshofer, Switzerland. - -## Licensing - -This code is licensed under MIT License. -https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE -(The file 'LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - -Some portions of the code have been derived from other projects. -All these projects require that we include a copyright notice, and some require that we also include some text of their -license file. - -fast_double_parser, Copyright (c) 2022 Daniel Lemire. BSL License. -https://github.com/lemire/fast_double_parser -https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL -(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - -fast_float, Copyright (c) 2021 The fast_float authors. MIT License. -https://github.com/fastfloat/fast_float -https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT -(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - -bigint, Copyright 2020 Tim Buktu. 2-clause BSD License. -https://github.com/tbuktu/bigint/tree/floatfft -https://github.com/tbuktu/bigint/blob/617c8cd8a7c5e4fb4d919c6a4d11e2586107f029/LICENSE -https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE -(We only use those portions of the bigint project that can be licensed under 2-clause BSD License.) -(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - --------------- Separator -------------- -Dependency: com.fasterxml.jackson.core:jackson-annotations -Copyright: FasterXML,LLC -=== Source URL: https://github.com/FasterXML/jackson-annotations/tree/jackson-annotations-2.17.0 -License: Apache 2.0 - ./LICENSE - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --------------- Separator -------------- - - ./src/main/resources/META-INF/NOTICE - -# Jackson JSON processor - -Jackson is a high-performance, Free/Open Source JSON processing library. -It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has -been in development since 2007. -It is currently developed by a community of developers. - -## Copyright - -Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) - -## Licensing - -Jackson 2.x core and extension components are licensed under Apache License 2.0 -To find the details that apply to this artifact see the accompanying LICENSE file. - -## Credits - -A list of contributors may be found from CREDITS(-2.x) file, which is included -in some artifacts (usually source distributions); but is always available -from the source code management (SCM) system project uses. - --------------- Separator -------------- -Dependency: com.fasterxml.jackson.core:jackson-core -Copyright: FasterXML,LLC -=== Source URL: https://github.com/FasterXML/jackson-core/tree/jackson-core-2.17.0 -License: Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./LICENSE - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./src/main/resources/META-INF/jackson-core-NOTICE - -# Jackson JSON processor - -Jackson is a high-performance, Free/Open Source JSON processing library. -It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has -been in development since 2007. -It is currently developed by a community of developers. - -## Copyright - -Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) - -## Licensing - -Jackson 2.x core and extension components are licensed under Apache License 2.0 -To find the details that apply to this artifact see the accompanying LICENSE file. - -## Credits - -A list of contributors may be found from CREDITS(-2.x) file, which is included -in some artifacts (usually source distributions); but is always available -from the source code management (SCM) system project uses. - -## FastDoubleParser - -jackson-core bundles a shaded copy of FastDoubleParser . -That code is available under an MIT license -under the following copyright. - -Copyright © 2023 Werner Randelshofer, Switzerland. MIT License. - -See FastDoubleParser-NOTICE for details of other source code included in FastDoubleParser -and the licenses and copyrights that apply to that code. - --------------- Separator -------------- -Dependency: com.fasterxml.jackson.core:jackson-databind -Copyright: FasterXML,LLC -=== Source URL: https://github.com/FasterXML/jackson-databind/tree/jackson-databind-2.17.0 -License: Apache 2.0 - ./LICENSE - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./src/main/resources/META-INF/NOTICE - -(Notice same as ./src/main/resources/META-INF/NOTICE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- -Dependency: io.projectreactor:reactor-core -Copyright: VMware Inc. -=== Source URL: https://github.com/reactor/reactor-core/tree/v3.7.0 -License: Apache 2.0 - ./codequality/spotless/licenseSlashstarStyle.txt - -/* - * Copyright (c) $YEAR VMware Inc. or its affiliates, All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - --------------- Separator -------------- - - ./LICENSE - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- -Dependency: net.bytebuddy:byte-buddy -Copyright: Rafael Winterhalter -=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy -License: Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./src/main/resources/META-INF/LICENSE - -This product bundles ASM ${version.asm}, which is available under a "3-clause BSD" -license. For details, see licenses/ASM. For more information visit ${asm.url}. - --------------- Separator -------------- -Dependency: net.bytebuddy:byte-buddy-dep -Copyright: Rafael Winterhalter -=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy-dep -License: Apache 2.0 - ./LICENSE - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - --------------- Separator -------------- - - ./NOTICE - -Copyright ${project.inceptionYear} - Present ${copyright.holder} - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - --------------- Separator -------------- -Dependency: org.ow2.asm:asm -Copyright: INRIA, France Telecom -=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm -License: BSD 3-Clause - ./LICENSE.txt - -BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) - --------------- Separator -------------- -Dependency: org.ow2.asm:asm-commons -Copyright: INRIA, France Telecom -=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-commons -License: BSD 3-Clause - ./LICENSE.txt - - - ASM: a very small and fast Java bytecode manipulation framework - Copyright (c) 2000-2011 INRIA, France Telecom - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. - --------------- Separator -------------- -Dependency: org.ow2.asm:asm-tree -Copyright: INRIA, France Telecom -=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-tree -License: BSD 3-Clause - ./LICENSE.txt - -BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) - --------------- Separator -------------- -Dependency: org.reactivestreams:reactive-streams -Copyright: Reactive Streams -=== Source URL: https://github.com/reactive-streams/reactive-streams-jvm/tree/v1.0.4 -License: MIT - ./LICENSE - -MIT No Attribution - -Copyright 2014 Reactive Streams - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --------------- Separator -------------- -Dependency: org.slf4j:slf4j-api -Copyright: QOS.ch -=== Source URL: https://github.com/qos-ch/slf4j/tree/v_2.0.16/slf4j-api -License: MIT - ./LICENSE.txt - -Copyright (c) 2004-2023 QOS.ch -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - - --------------- Separator -------------- - - - -=== Entry created by OSCS on 2025-07-03 09:39:14 - +------------------------ Third-Party Components --------------------------------- +MCP Java SDK 0.10.0 + +*************************** +------------------------------- License and Notices for MCP Java SDK + + +Copyright: the original author or authors +=== Source URL: https://github.com/modelcontextprotocol/java-sdk/tree/v0.10.0/mcp +License: MIT + + ./LICENSE + +MIT License + +Copyright (c) 2025 the original author or authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------- Separator -------------- + +----------------------------------- Fourth-party dependencies ------------------------------------ + +Dependency: ch.randelshofer:fastdoubleparser +Copyright: Werner Randelshofer +=== Source URL: https://github.com/wrandelshofer/FastDoubleParser/tree/v1.0.0/fastdoubleparser +License: MIT + === https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE + +Copyright 2022 Tim Buktu + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------- Separator -------------- + + === https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL + +Copyright (c) Daniel Lemire + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +-------------- Separator -------------- + + === https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT + +MIT License + +Copyright (c) 2021 The fast_float authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +-------------- Separator -------------- + + ./LICENSE + +MIT License + +Copyright (c) 2023 Werner Randelshofer, Switzerland. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------- Separator -------------- + + === https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE + +MIT License + +Copyright (c) 2023 Werner Randelshofer, Switzerland. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------- Separator -------------- + + ./NOTICE + +# FastDoubleParser + +This is a Java port of Daniel Lemire's fast_float project. +This project provides parsers for double, float, BigDecimal and BigInteger values. + +## Copyright + +Copyright © 2023 Werner Randelshofer, Switzerland. + +## Licensing + +This code is licensed under MIT License. +https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE +(The file 'LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +Some portions of the code have been derived from other projects. +All these projects require that we include a copyright notice, and some require that we also include some text of their +license file. + +fast_double_parser, Copyright (c) 2022 Daniel Lemire. BSL License. +https://github.com/lemire/fast_double_parser +https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +fast_float, Copyright (c) 2021 The fast_float authors. MIT License. +https://github.com/fastfloat/fast_float +https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +bigint, Copyright 2020 Tim Buktu. 2-clause BSD License. +https://github.com/tbuktu/bigint/tree/floatfft +https://github.com/tbuktu/bigint/blob/617c8cd8a7c5e4fb4d919c6a4d11e2586107f029/LICENSE +https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE +(We only use those portions of the bigint project that can be licensed under 2-clause BSD License.) +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +-------------- Separator -------------- +Dependency: com.fasterxml.jackson.core:jackson-annotations +Copyright: FasterXML,LLC +=== Source URL: https://github.com/FasterXML/jackson-annotations/tree/jackson-annotations-2.17.0 +License: Apache 2.0 + ./LICENSE + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +-------------- Separator -------------- + + ./src/main/resources/META-INF/NOTICE + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Copyright + +Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) + +## Licensing + +Jackson 2.x core and extension components are licensed under Apache License 2.0 +To find the details that apply to this artifact see the accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS(-2.x) file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +-------------- Separator -------------- +Dependency: com.fasterxml.jackson.core:jackson-core +Copyright: FasterXML,LLC +=== Source URL: https://github.com/FasterXML/jackson-core/tree/jackson-core-2.17.0 +License: Apache 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./LICENSE + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./src/main/resources/META-INF/jackson-core-NOTICE + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Copyright + +Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) + +## Licensing + +Jackson 2.x core and extension components are licensed under Apache License 2.0 +To find the details that apply to this artifact see the accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS(-2.x) file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +## FastDoubleParser + +jackson-core bundles a shaded copy of FastDoubleParser . +That code is available under an MIT license +under the following copyright. + +Copyright © 2023 Werner Randelshofer, Switzerland. MIT License. + +See FastDoubleParser-NOTICE for details of other source code included in FastDoubleParser +and the licenses and copyrights that apply to that code. + +-------------- Separator -------------- +Dependency: com.fasterxml.jackson.core:jackson-databind +Copyright: FasterXML,LLC +=== Source URL: https://github.com/FasterXML/jackson-databind/tree/jackson-databind-2.17.0 +License: Apache 2.0 + ./LICENSE + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./src/main/resources/META-INF/NOTICE + +(Notice same as ./src/main/resources/META-INF/NOTICE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- +Dependency: io.projectreactor:reactor-core +Copyright: VMware Inc. +=== Source URL: https://github.com/reactor/reactor-core/tree/v3.7.0 +License: Apache 2.0 + ./codequality/spotless/licenseSlashstarStyle.txt + +/* + * Copyright (c) $YEAR VMware Inc. or its affiliates, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +-------------- Separator -------------- + + ./LICENSE + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- +Dependency: net.bytebuddy:byte-buddy +Copyright: Rafael Winterhalter +=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy +License: Apache 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + +Apache 2.0 + ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) + +-------------- Separator -------------- + + ./src/main/resources/META-INF/LICENSE + +This product bundles ASM ${version.asm}, which is available under a "3-clause BSD" +license. For details, see licenses/ASM. For more information visit ${asm.url}. + +-------------- Separator -------------- +Dependency: net.bytebuddy:byte-buddy-dep +Copyright: Rafael Winterhalter +=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy-dep +License: Apache 2.0 + ./LICENSE + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +-------------- Separator -------------- + + ./NOTICE + +Copyright ${project.inceptionYear} - Present ${copyright.holder} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +-------------- Separator -------------- +Dependency: org.ow2.asm:asm +Copyright: INRIA, France Telecom +=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm +License: BSD 3-Clause + ./LICENSE.txt + +BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) + +-------------- Separator -------------- +Dependency: org.ow2.asm:asm-commons +Copyright: INRIA, France Telecom +=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-commons +License: BSD 3-Clause + ./LICENSE.txt + + + ASM: a very small and fast Java bytecode manipulation framework + Copyright (c) 2000-2011 INRIA, France Telecom + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +-------------- Separator -------------- +Dependency: org.ow2.asm:asm-tree +Copyright: INRIA, France Telecom +=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-tree +License: BSD 3-Clause + ./LICENSE.txt + +BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) + +-------------- Separator -------------- +Dependency: org.reactivestreams:reactive-streams +Copyright: Reactive Streams +=== Source URL: https://github.com/reactive-streams/reactive-streams-jvm/tree/v1.0.4 +License: MIT + ./LICENSE + +MIT No Attribution + +Copyright 2014 Reactive Streams + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------- Separator -------------- +Dependency: org.slf4j:slf4j-api +Copyright: QOS.ch +=== Source URL: https://github.com/qos-ch/slf4j/tree/v_2.0.16/slf4j-api +License: MIT + ./LICENSE.txt + +Copyright (c) 2004-2023 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +-------------- Separator -------------- + + + +=== Entry created by OSCS on 2025-07-03 09:39:14 + diff --git a/src/ojdbc-log-analyzer-mcp-server/pom.xml b/src/oracle-db-toolbox-mcp-server/pom.xml similarity index 91% rename from src/ojdbc-log-analyzer-mcp-server/pom.xml rename to src/oracle-db-toolbox-mcp-server/pom.xml index 7cedb7cb..f47b4921 100644 --- a/src/ojdbc-log-analyzer-mcp-server/pom.xml +++ b/src/oracle-db-toolbox-mcp-server/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.oracle.database.jdbc - ojdbc-log-analyzer-mcp-server + oracle-db-toolbox-mcp-server 1.0.0 jar Oracle JDBC Log Analyzer MCP Server @@ -31,6 +31,7 @@ 17 0.12.1 23.9.0.25.07 + 2.5 1.0.0 5.10.0 ${java.version} @@ -52,6 +53,12 @@ ${ojdbc.version} + + org.yaml + snakeyaml + ${snakeYaml.version} + + com.oracle.database.jdbc ojdbc-log-analyzer @@ -82,7 +89,7 @@ false - com.oracle.database.jdbc.OracleJDBCLogAnalyzerMCPServer + com.oracle.database.jdbc.OracleDBToolboxMCPServer diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java new file mode 100644 index 00000000..9a06b497 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -0,0 +1,158 @@ +package com.oracle.database.jdbc; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.oracle.database.jdbc.config.ConfigRoot; +import com.oracle.database.jdbc.config.SourceConfig; +import com.oracle.database.jdbc.config.ToolConfig; +import com.oracle.database.jdbc.config.ToolParameterConfig; +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.server.McpSyncServer; +import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; +import io.modelcontextprotocol.spec.McpSchema; +import org.yaml.snakeyaml.Yaml; + +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.oracle.database.jdbc.Utils.errorResult; +import static com.oracle.database.jdbc.Utils.installExternalExtensionsFromDir; +import static com.oracle.database.jdbc.Utils.rsToList; + +public class OracleDBToolboxMCPServer { + + static ServerConfig config; + private static final ObjectMapper JSON = new ObjectMapper(); + + static { + String configFilePath = System.getProperty("configFile"); + ConfigRoot yamlConfig = null; + try { + try (Reader reader = Files.newBufferedReader(Paths.get(configFilePath))) { + Yaml yaml = new Yaml(); + yamlConfig = yaml.loadAs(reader, ConfigRoot.class); + } + } catch (Exception e) { + Logger logger = Logger.getLogger(OracleDBToolboxMCPServer.class.getName()); + logger.log(Level.SEVERE, e.getMessage()); + } + if (yamlConfig == null) { + config = ServerConfig.fromSystemProperties(); + } else { + String defaultSourceKey = yamlConfig.sources.keySet().stream().findFirst().orElseThrow(); + config = ServerConfig.fromSystemPropertiesAndYaml(yamlConfig, defaultSourceKey); + if (config.tools != null) { + for (Map.Entry entry : config.tools.entrySet()) { + entry.getValue().name = entry.getKey(); + } + } + } + } + + private static final String SQL_ONLY = """ + { + "type":"object", + "properties": { + "sql": { + "type": "string" + }, + "txId": { + "type": "string", + "description": "Optional active transaction id" + } + }, + "required":["sql"] + }"""; + + public static void main(String[] args) { + installExternalExtensionsFromDir(); + + McpSyncServer server = McpServer.sync(new StdioServerTransportProvider(new ObjectMapper())) + .serverInfo("ojdbc-log-analyzer-mcp-server", "1.0.0") + .capabilities(McpSchema.ServerCapabilities.builder() + .tools(true) + .logging() + .build()) + .build(); + addSyncToolSpecifications(server); + } + + private OracleDBToolboxMCPServer() { + // Prevent instantiation + } + + /** + *

+ * Returns the list of all available SyncToolSpecification instances for this server. + *

+ */ + public static void addSyncToolSpecifications(McpSyncServer server) { + // ---------- Dynamically Added Tools ---------- + for (Map.Entry entry : config.tools.entrySet()) { + ToolConfig tc = entry.getValue(); + server.addTool( + McpServerFeatures.SyncToolSpecification.builder() + .tool(McpSchema.Tool.builder() + .name(tc.name) + .title(tc.name) + .description(tc.description) + .inputSchema(SQL_ONLY) + .build() + ) + .callHandler((exchange, callReq) -> { + // Resolve source + SourceConfig src = config.sources.get(tc.source); + String jdbcUrl = (src != null) ? src.toJdbcUrl() : config.dbUrl; + String dbUser = (src != null) ? src.user : config.dbUser; + String dbPassword = (src != null) ? src.password : config.dbPassword; + + // Obtain a connection for this invocation + try (Connection c = DriverManager.getConnection(jdbcUrl, dbUser, dbPassword)) { + // Prepare statement and parameters + PreparedStatement ps = c.prepareStatement(tc.statement); + // map callReq.arguments() to tc.parameters + int paramIdx = 1; + if (tc.parameters != null) { + for (ToolParameterConfig param : tc.parameters) { + Object argVal = callReq.arguments().get(param.name); + ps.setObject(paramIdx++, argVal); + } + } + + if (tc.statement.trim().toLowerCase().startsWith("select")) { + ResultSet rs = ps.executeQuery(); + List> rows = rsToList(rs); + return McpSchema.CallToolResult.builder() + .structuredContent(Map.of("rows", rows, "rowCount", rows.size())) + .addTextContent(JSON.writeValueAsString(rows)) + .build(); + } else { + int n = ps.executeUpdate(); + return McpSchema.CallToolResult.builder() + .structuredContent(Map.of("updateCount", n)) + .addTextContent("{\"updateCount\":" + n + "}") + .build(); + } + } catch (Exception ex) { + return errorResult(tc.name, ex); + } + }) + .build() + ); + } + List specs = OracleJDBCLogAnalyzerMCPServer.getLogAnalyzerTools(); + for (McpServerFeatures.SyncToolSpecification spec : specs) { + server.addTool(spec); + } + } + +} diff --git a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java similarity index 80% rename from src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java rename to src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java index 78bec58f..bdeb0fbe 100644 --- a/src/ojdbc-log-analyzer-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java @@ -8,20 +8,22 @@ package com.oracle.database.jdbc; import com.fasterxml.jackson.databind.ObjectMapper; +import com.oracle.database.jdbc.logs.model.JDBCConnectionEvent; +import com.oracle.database.jdbc.logs.model.JDBCExecutedQuery; +import com.oracle.database.jdbc.logs.model.LogError; +import com.oracle.database.jdbc.logs.model.RDBMSError; +import com.oracle.database.jdbc.logs.model.RDBMSPacketDump; import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.Tool; import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.TextContent; -import oracle.jdbc.logs.analyzer.JDBCLog; -import oracle.jdbc.logs.analyzer.RDBMSLog; -import oracle.jdbc.logs.model.JDBCConnectionEvent; -import oracle.jdbc.logs.model.JDBCExecutedQuery; -import oracle.jdbc.logs.model.LogError; -import oracle.jdbc.logs.model.RDBMSError; -import oracle.jdbc.logs.model.RDBMSPacketDump; + +import com.oracle.database.jdbc.logs.analyzer.JDBCLog; +import com.oracle.database.jdbc.logs.analyzer.RDBMSLog; import java.io.File; import java.io.IOException; @@ -37,84 +39,20 @@ */ public final class OracleJDBCLogAnalyzerMCPServer { - public static void main(String[] args) { - McpServer.sync(new StdioServerTransportProvider(new ObjectMapper())) - .serverInfo("ojdbc-log-analyzer-mcp-server", "1.0.0") - .capabilities(McpSchema.ServerCapabilities.builder() - .tools(true) - .logging() - .build()) - .tools(getSyncToolSpecifications()) - .build(); - } - private static final String FILE_PATH = "filePath"; private static final String SECOND_FILE_PATH = "secondFilePath"; private static final String CONNECTION_ID = "connectionId"; - private static final String FILE_PATH_SCHEMA = """ - { - "type": "object", - "properties": { - "filePath": { - "type": "string", - "description": "Absolute path or an URL to the Oracle JDBC log file." - } - }, - "required": ["filePath"] - } - """; - private static final String FILE_COMPARISON_SCHEMA = """ - { - "type": "object", - "properties": { - "filePath": { - "type": "string", - "description": "Absolute path or an URL to the 1st Oracle JDBC log file" - }, - "secondFilePath": { - "type": "string", - "description": "Absolute path or an URL to the 2nd Oracle JDBC log file" - } - }, - "required": ["filePath", "secondFilePath"] - } - """; - private static final String RDBMS_TOOLS_SCHEMA = """ - { - "type": "object", - "properties": { - "filePath": { - "type": "string", - "description": "Absolute path or an URL to the RDBMS/SQLNet trace file" - }, - "connectionId": { - "type": "string", - "description": "Connection ID string" - } - }, - "required": ["filePath", "connectionId"] - } - """; - private OracleJDBCLogAnalyzerMCPServer() { - // Prevent instantiation - } - - /** - *

- * Returns the list of all available SyncToolSpecification instances for this server. - *

- */ - public static List getSyncToolSpecifications() { + public static List getLogAnalyzerTools() { return List.of( - getStatsTool(), - getQueriesTool(), - getErrorsTool(), - getListLogsDirectoryTool(), - getConnectionEventsTool(), - logComparisonTool(), - getRdbmsErrorsTool(), - getPacketDumpsTool()); + getStatsTool(), + getQueriesTool(), + getErrorsTool(), + getListLogsDirectoryTool(), + getConnectionEventsTool(), + logComparisonTool(), + getRdbmsErrorsTool(), + getPacketDumpsTool()); } /** @@ -133,7 +71,7 @@ private static SyncToolSpecification getStatsTool() { .name("get-stats") .title("Get JDBC Stats") .description("Return aggregated stats (error count, packets, bytes) from an Oracle JDBC thin log.") - .inputSchema(FILE_PATH_SCHEMA) + .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall( () -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); @@ -158,7 +96,7 @@ private static SyncToolSpecification getQueriesTool() { .name("get-queries") .title("Get JDBC Queries") .description("Get all executed queries from an Oracle JDBC thin log file, including the timestamp and execution time.") - .inputSchema(FILE_PATH_SCHEMA) + .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); @@ -186,7 +124,7 @@ private static SyncToolSpecification getErrorsTool() { .name("get-errors") .title("Get JDBC Errors") .description("Get all reported errors from an Oracle JDBC thin log file, including stacktrace and log context.") - .inputSchema(FILE_PATH_SCHEMA) + .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); @@ -213,7 +151,7 @@ private static SyncToolSpecification getListLogsDirectoryTool() { .name("list-log-files-from-directory") .title("List Files From Directory") .description("List all visible files from a specified directory, which helps the user analyze multiple files with one prompt.") - .inputSchema(FILE_PATH_SCHEMA) + .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var directoryPath = String.valueOf(callReq.arguments().get(FILE_PATH)); @@ -245,7 +183,7 @@ private static SyncToolSpecification logComparisonTool() { .name("log-comparison") .title("JDBC Log Comparison") .description("Compare two JDBC log files for performance metrics, errors, and network information.") - .inputSchema(FILE_COMPARISON_SCHEMA) + .inputSchema(ToolSchemas.FILE_COMPARISON_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); @@ -269,7 +207,7 @@ private static SyncToolSpecification getConnectionEventsTool() { .name("get-connection-events") .title("Get JDBC Connection Events") .description("Retrieve opened and closed JDBC connection events from the log file with timestamp and connection details.") - .inputSchema(FILE_PATH_SCHEMA) + .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); @@ -297,7 +235,7 @@ private static SyncToolSpecification getRdbmsErrorsTool() { .name("get-rdbms-errors") .title("Get RDBMS/SQLNet Errors") .description("Retrieve errors from an RDBMS/SQLNet trace file.") - .inputSchema(RDBMS_TOOLS_SCHEMA) + .inputSchema(ToolSchemas.RDBMS_TOOLS_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var logFile = String.valueOf(callReq.arguments().get(FILE_PATH)); @@ -324,7 +262,7 @@ private static SyncToolSpecification getPacketDumpsTool() { .name("get-packet-dumps") .title("Get RDBMS/SQLNet Packet Dumps") .description("Extract packet dumps from RDBMS/SQLNet trace file for given connection ID.") - .inputSchema(RDBMS_TOOLS_SCHEMA) + .inputSchema(ToolSchemas.RDBMS_TOOLS_SCHEMA) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java new file mode 100644 index 00000000..72fc2cd5 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java @@ -0,0 +1,172 @@ +package com.oracle.database.jdbc; + +import com.oracle.database.jdbc.config.ConfigRoot; +import com.oracle.database.jdbc.config.SourceConfig; +import com.oracle.database.jdbc.config.ToolConfig; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * Immutable server configuration loaded from system properties. + * + *

Conditionally required property:

+ *
    + *
  • {@code db.url} — required only when any database tool is enabled.
  • + *
+ * + *

Optional properties:

+ *
    + *
  • {@code db.user}
  • + *
  • {@code db.password}
  • + *
  • {@code tools} — comma-separated allow-list of tool names; {@code *} or {@code all} enables all.
  • + *
+ * + *

Use {@link #fromSystemProperties()} to create an instance with validation and defaults.

+ */ +final class ServerConfig { + public final String dbUrl; + public final String dbUser; + public final String dbPassword; + public final Set toolsFilter; + public final Map sources; + public final Map tools; + + private ServerConfig( + String dbUrl, + String dbUser, + String dbPassword, + Set toolsFilter, + Map sources, + Map tools + ) { + this.dbUrl = dbUrl; + this.dbUser = dbUser; + this.dbPassword = dbPassword; + this.toolsFilter = toolsFilter; + this.sources = sources; + this.tools = tools; + } + + private static final Set DB_TOOLS = Set.of( + "execute_query","read_query","write_query","create_table","delete_table", + "list_tables","describe_table","start_transaction","resume_transaction", + "commit_transaction","rollback_transaction","exec_in_tx","db_ping", + "db_metrics_range","similarity_search" + ); + + + /** + * Builds a {@link ServerConfig} from JVM system properties (i.e., {@code -Dkey=value}), + * with fallback to values from a parsed YAML configuration. + *

+ * Resolution order for each property: + *

    + *
  1. JVM system property (highest priority, e.g., {@code -Ddb.url}, {@code -Ddb.user}, {@code -Ddb.password})
  2. + *
  3. YAML config ({@link ConfigRoot} and specified source), if system property is absent or blank
  4. + *
+ *

+ * Validates that all required values are present if any database tool is enabled. + * + * @param configRoot the parsed YAML configuration root (nullable if not used) + * @param defaultSourceKey the source key in YAML to use for Oracle connection details fallback + * @return a validated configuration + * @throws IllegalStateException if required properties are missing from both system properties and YAML config + */ + public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, String defaultSourceKey) { + Set tools = parseToolsProp(System.getProperty("tools")); + boolean needDb = wantsAnyDbTools(tools); + + String dbUrl = System.getProperty("db.url"); + String dbUser = System.getProperty("db.user"); + String dbPass = System.getProperty("db.password"); + + Map sources = configRoot != null ? configRoot.sources : Collections.emptyMap(); + Map toolsMap = configRoot != null ? configRoot.tools : Collections.emptyMap(); + + if ((dbUrl == null || dbUrl.isBlank() || + dbUser == null || dbUser.isBlank() || + dbPass == null || dbPass.isBlank()) + && sources.containsKey(defaultSourceKey)) { + SourceConfig src = sources.get(defaultSourceKey); + if (dbUrl == null || dbUrl.isBlank()) dbUrl = src.toJdbcUrl(); + if (dbUser == null || dbUser.isBlank()) dbUser = src.user; + if (dbPass == null || dbPass.isBlank()) dbPass = src.password; + } + + if (needDb && (dbUrl == null || dbUrl.isBlank())) { + throw new IllegalStateException("Missing required db.url in both system properties and YAML config"); + } + if (needDb && (dbUser == null || dbUser.isBlank())) { + throw new IllegalStateException("Missing required db.user in both system properties and YAML config"); + } + if (needDb && (dbPass == null || dbPass.isBlank())) { + throw new IllegalStateException("Missing required db.password in both system properties and YAML config"); + } + + return new ServerConfig(dbUrl, dbUser, dbPass, tools, sources, toolsMap); + } + + /** + * Builds a {@link ServerConfig} from JVM system properties(i.e., {@code -Dkey=value}). + * Validates required properties and applies sensible defaults for optional ones. + * + * @return a validated configuration + * @throws IllegalStateException if {@code db.url} is missing or blank + */ + static ServerConfig fromSystemProperties() { + Set tools = parseToolsProp(System.getProperty("tools")); + boolean needDb = wantsAnyDbTools(tools); + + String dbUrl = System.getProperty("db.url"); + if (needDb && (dbUrl == null || dbUrl.isBlank())) { + throw new IllegalStateException("Missing required system property: db.url"); + } + + return new ServerConfig( + dbUrl, + System.getProperty("db.user"), + System.getProperty("db.password"), + tools, + new HashMap<>(), + new HashMap<>() + ); + } + + /** + * Reads a comma-separated list of tool names and returns which tools + * should be enabled. + * If the input is empty, missing, "*" or "all", it means + * “enable every tool” and returns {@code null}. + * + * Examples: + * "read_query,write_query" -> ["read_query","write_query"] + * "*" or "all" or "" -> null (treat as all tools enabled) + * + * @param prop comma-separated tool names + * @return a set of enabled tool names, or {@code null} to mean “all tools” + */ + private static Set parseToolsProp(String prop) { + if (prop == null || prop.isBlank()) return null; // null = allow all + Set s = new LinkedHashSet<>(); + for (String t : prop.split(",")) { + String k = t.trim().toLowerCase(Locale.ROOT); + if (!k.isEmpty()) s.add(k); + } + if (s.contains("*") || s.contains("all")) return null; // treat as allow all + return s; + } + + private static boolean wantsAnyDbTools(Set toolsFilter) { + if (toolsFilter == null) return true; // null == all tools enabled + for (String t : toolsFilter) { + if (DB_TOOLS.contains(t)) return true; + } + return false; + } + +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java new file mode 100644 index 00000000..215f7d12 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java @@ -0,0 +1,8 @@ +package com.oracle.database.jdbc.config; + +import java.util.Map; + +public class ConfigRoot { + public Map sources; + public Map tools; +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java new file mode 100644 index 00000000..31956f28 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java @@ -0,0 +1,18 @@ +package com.oracle.database.jdbc.config; + +public class SourceConfig { + public String host; + public int port; + public String database; // Oracle SID or service name + public String url; + public String user; + public String password; + + public String toJdbcUrl() { + if(url == null) { + return String.format("jdbc:oracle:thin:@%s:%d/%s", host, port, database); + } else { + return url; + } + } +} \ No newline at end of file diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java new file mode 100644 index 00000000..8b723bfa --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java @@ -0,0 +1,11 @@ +package com.oracle.database.jdbc.config; + +import java.util.List; + +public class ToolConfig { + public String name; // The tool name (from YAML key) + public String source; // Reference key from sources + public String description; + public List parameters; + public String statement; // The SQL statement to execute +} \ No newline at end of file diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java new file mode 100644 index 00000000..9145e8fb --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java @@ -0,0 +1,7 @@ +package com.oracle.database.jdbc.config; + +public class ToolParameterConfig { + public String name; + public String type; + public String description; +} \ No newline at end of file diff --git a/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java similarity index 97% rename from src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java rename to src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java index 223b764a..82411138 100644 --- a/src/ojdbc-log-analyzer-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java +++ b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java @@ -20,7 +20,7 @@ class OracleJDBCLogAnalyzerMCPServerTest { @BeforeAll static void initializeTools(){ - tools = OracleJDBCLogAnalyzerMCPServer.getSyncToolSpecifications() + tools = OracleJDBCLogAnalyzerMCPServer.getLogAnalyzerTools() .stream() .map(SyncToolSpecification::tool) .collect(Collectors.toMap(McpSchema.Tool::name, identity())); From 351d94681bf80ec5e1a290e36af5824b2d6c66a5 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 20 Nov 2025 15:56:52 +0100 Subject: [PATCH 10/77] more refactoring --- .../jdbc/OracleDBToolboxMCPServer.java | 17 +-- .../com/oracle/database/jdbc/ToolSchemas.java | 64 ++++++++ .../java/com/oracle/database/jdbc/Utils.java | 142 ++++++++++++++++++ 3 files changed, 207 insertions(+), 16 deletions(-) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 9a06b497..71f960c3 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -58,21 +58,6 @@ public class OracleDBToolboxMCPServer { } } - private static final String SQL_ONLY = """ - { - "type":"object", - "properties": { - "sql": { - "type": "string" - }, - "txId": { - "type": "string", - "description": "Optional active transaction id" - } - }, - "required":["sql"] - }"""; - public static void main(String[] args) { installExternalExtensionsFromDir(); @@ -105,7 +90,7 @@ public static void addSyncToolSpecifications(McpSyncServer server) { .name(tc.name) .title(tc.name) .description(tc.description) - .inputSchema(SQL_ONLY) + .inputSchema(ToolSchemas.SQL_ONLY) .build() ) .callHandler((exchange, callReq) -> { diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java new file mode 100644 index 00000000..dc2d34a8 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java @@ -0,0 +1,64 @@ +package com.oracle.database.jdbc; + +public class ToolSchemas { + + static final String SQL_ONLY = """ + { + "type":"object", + "properties": { + "sql": { + "type": "string" + }, + "txId": { + "type": "string", + "description": "Optional active transaction id" + } + }, + "required":["sql"] + }"""; + + static final String FILE_PATH_SCHEMA = """ + { + "type": "object", + "properties": { + "filePath": { + "type": "string", + "description": "Absolute path or an URL to the Oracle JDBC log file." + } + }, + "required": ["filePath"] + } + """; + static final String FILE_COMPARISON_SCHEMA = """ + { + "type": "object", + "properties": { + "filePath": { + "type": "string", + "description": "Absolute path or an URL to the 1st Oracle JDBC log file" + }, + "secondFilePath": { + "type": "string", + "description": "Absolute path or an URL to the 2nd Oracle JDBC log file" + } + }, + "required": ["filePath", "secondFilePath"] + } + """; + static final String RDBMS_TOOLS_SCHEMA = """ + { + "type": "object", + "properties": { + "filePath": { + "type": "string", + "description": "Absolute path or an URL to the RDBMS/SQLNet trace file" + }, + "connectionId": { + "type": "string", + "description": "Connection ID string" + } + }, + "required": ["filePath", "connectionId"] + } + """; +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java new file mode 100644 index 00000000..8da93198 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -0,0 +1,142 @@ +package com.oracle.database.jdbc; + +import io.modelcontextprotocol.spec.McpSchema; + +import java.io.IOException; +import java.io.Reader; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.Provider; +import java.security.Security; +import java.sql.Clob; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Stream; + +public class Utils { + + static McpSchema.CallToolResult errorResult(String toolName, Exception e) { + String msg = e.getMessage() != null ? e.getMessage() : e.toString(); + return McpSchema.CallToolResult.builder() + .isError(true) + .addTextContent("Error in " + toolName + ": " + msg) + .build(); + } + + /** + * Loads external JARs from the directory given by + * the system property {@code ojdbc.ext.dir} and makes them available + * to the application at runtime. + * + *

Behavior:

+ *
    + *
  • If the property is missing/blank, does nothing.
  • + *
  • Recursively scans the directory for {@code .jar} files.
  • + *
  • Adds all found JARs to a temporary class loader and activates it.
  • + *
  • Logs basic problems (invalid dir, scan failures, no JARs found).
  • + *
+ * + */ + static void installExternalExtensionsFromDir() { + final String dir = System.getProperty("ojdbc.ext.dir"); + if (dir == null || dir.isBlank()) { + return; + } + + final Path root = Paths.get(dir); + if (!Files.isDirectory(root)) { + System.err.println("[mcp-ojdbc] ojdbc.ext.dir is not a directory: " + dir); + return; + } + final List jarUrls = new ArrayList<>(); + try (Stream walk = Files.walk(root)) { + walk.filter(p -> Files.isRegularFile(p) && p.toString().toLowerCase(Locale.ROOT).endsWith(".jar")) + .forEach(p -> { + try { + jarUrls.add(p.toUri().toURL()); + } catch (Exception e) { + System.err.println("[mcp-ojdbc] Failed to add jar: " + p + " -> " + e); + } + }); + } catch (Exception e) { + System.err.println("[mcp-ojdbc] Failed to scan " + dir + " -> " + e); + return; + } + + if (jarUrls.isEmpty()) { + System.err.println("[mcp-ojdbc] No jars found under " + dir); + return; + } + + final ClassLoader previousTccl = Thread.currentThread().getContextClassLoader(); + final URLClassLoader ucl = new URLClassLoader(jarUrls.toArray(new URL[0]), previousTccl); + Thread.currentThread().setContextClassLoader(ucl); + + try { + Class providerClass = Class.forName("oracle.security.pki.OraclePKIProvider", true, ucl); + Provider provider = (Provider) providerClass.getDeclaredConstructor().newInstance(); + Security.addProvider(provider); + } catch (Throwable ignored) {} + + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { Thread.currentThread().setContextClassLoader(previousTccl); } catch (Throwable ignored) {} + try { ucl.close(); } catch (Throwable ignored) {} + })); + } + + /** + * Converts a ResultSet into a list of maps (rows). + * + * @param rs a valid ResultSet + * @return list of rows with column:value mapping + * @throws SQLException if reading from ResultSet fails + */ + static List> rsToList(ResultSet rs) throws SQLException { + List> out = new ArrayList<>(); + ResultSetMetaData md = rs.getMetaData(); + int cols = md.getColumnCount(); + while (rs.next()) { + Map row = new LinkedHashMap<>(); + for (int i = 1; i <= cols; i++) { + String colName = md.getColumnLabel(i); + Object value = rs.getObject(i); + + if (value instanceof Clob clob) { + value = clobToString(clob); + } + + row.put(colName, value); + } + out.add(row); + } + return out; + } + + /** + * Safely converts a CLOB to String. + */ + private static String clobToString(Clob clob) throws SQLException { + if (clob == null) + return null; + StringBuilder sb = new StringBuilder(); + try (Reader reader = clob.getCharacterStream()) { + char[] buf = new char[4096]; + int len; + while ((len = reader.read(buf)) != -1) { + sb.append(buf, 0, len); + } + } catch (IOException e) { + throw new SQLException("Failed to read CLOB", e); + } + return sb.toString(); + } +} From 22a11bda3ca702b5aea9cf7352f6b8fa29164ab8 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 20 Nov 2025 19:54:59 +0100 Subject: [PATCH 11/77] Code refactoring --- src/oracle-db-toolbox-mcp-server/README.md | 8 +- .../jdbc/OracleDBToolboxMCPServer.java | 111 +------------ .../jdbc/OracleJDBCLogAnalyzerMCPServer.java | 108 ++++++------- .../java/com/oracle/database/jdbc/Utils.java | 147 +++++++++++++++++- 4 files changed, 200 insertions(+), 174 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 887509d4..ddfcb845 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -1,8 +1,8 @@ -# Oracle JDBC Log Analyzer MCP Server +# Oracle DB Toolbox MCP Server ## Overview -The `ojdbc-log-analyzer-mcp-server` provides 8 tools for analyzing Oracle JDBC thin client logs and RDBMS/SQLNet trace files: +The `oracle-db-toolbox-mcp-server` provides the capability to build your own custom tools along with 8 tools for analyzing Oracle JDBC thin client logs and RDBMS/SQLNet trace files: ### Oracle JDBC Log Analysis: @@ -69,6 +69,10 @@ sources: tools: hotels-by-name: source: prod-db + parameters: + - name: name + type: string + description: Hotel name to search for. statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' ``` To enable YAML configuration, launch the server with: diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 71f960c3..7ce79764 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -1,61 +1,19 @@ package com.oracle.database.jdbc; import com.fasterxml.jackson.databind.ObjectMapper; -import com.oracle.database.jdbc.config.ConfigRoot; -import com.oracle.database.jdbc.config.SourceConfig; -import com.oracle.database.jdbc.config.ToolConfig; -import com.oracle.database.jdbc.config.ToolParameterConfig; import io.modelcontextprotocol.server.McpServer; -import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; -import org.yaml.snakeyaml.Yaml; -import java.io.Reader; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.oracle.database.jdbc.Utils.errorResult; import static com.oracle.database.jdbc.Utils.installExternalExtensionsFromDir; -import static com.oracle.database.jdbc.Utils.rsToList; public class OracleDBToolboxMCPServer { static ServerConfig config; - private static final ObjectMapper JSON = new ObjectMapper(); static { - String configFilePath = System.getProperty("configFile"); - ConfigRoot yamlConfig = null; - try { - try (Reader reader = Files.newBufferedReader(Paths.get(configFilePath))) { - Yaml yaml = new Yaml(); - yamlConfig = yaml.loadAs(reader, ConfigRoot.class); - } - } catch (Exception e) { - Logger logger = Logger.getLogger(OracleDBToolboxMCPServer.class.getName()); - logger.log(Level.SEVERE, e.getMessage()); - } - if (yamlConfig == null) { - config = ServerConfig.fromSystemProperties(); - } else { - String defaultSourceKey = yamlConfig.sources.keySet().stream().findFirst().orElseThrow(); - config = ServerConfig.fromSystemPropertiesAndYaml(yamlConfig, defaultSourceKey); - if (config.tools != null) { - for (Map.Entry entry : config.tools.entrySet()) { - entry.getValue().name = entry.getKey(); - } - } - } + config = Utils.loadConfig(); } public static void main(String[] args) { @@ -68,76 +26,11 @@ public static void main(String[] args) { .logging() .build()) .build(); - addSyncToolSpecifications(server); + Utils.addSyncToolSpecifications(server, config); } private OracleDBToolboxMCPServer() { // Prevent instantiation } - /** - *

- * Returns the list of all available SyncToolSpecification instances for this server. - *

- */ - public static void addSyncToolSpecifications(McpSyncServer server) { - // ---------- Dynamically Added Tools ---------- - for (Map.Entry entry : config.tools.entrySet()) { - ToolConfig tc = entry.getValue(); - server.addTool( - McpServerFeatures.SyncToolSpecification.builder() - .tool(McpSchema.Tool.builder() - .name(tc.name) - .title(tc.name) - .description(tc.description) - .inputSchema(ToolSchemas.SQL_ONLY) - .build() - ) - .callHandler((exchange, callReq) -> { - // Resolve source - SourceConfig src = config.sources.get(tc.source); - String jdbcUrl = (src != null) ? src.toJdbcUrl() : config.dbUrl; - String dbUser = (src != null) ? src.user : config.dbUser; - String dbPassword = (src != null) ? src.password : config.dbPassword; - - // Obtain a connection for this invocation - try (Connection c = DriverManager.getConnection(jdbcUrl, dbUser, dbPassword)) { - // Prepare statement and parameters - PreparedStatement ps = c.prepareStatement(tc.statement); - // map callReq.arguments() to tc.parameters - int paramIdx = 1; - if (tc.parameters != null) { - for (ToolParameterConfig param : tc.parameters) { - Object argVal = callReq.arguments().get(param.name); - ps.setObject(paramIdx++, argVal); - } - } - - if (tc.statement.trim().toLowerCase().startsWith("select")) { - ResultSet rs = ps.executeQuery(); - List> rows = rsToList(rs); - return McpSchema.CallToolResult.builder() - .structuredContent(Map.of("rows", rows, "rowCount", rows.size())) - .addTextContent(JSON.writeValueAsString(rows)) - .build(); - } else { - int n = ps.executeUpdate(); - return McpSchema.CallToolResult.builder() - .structuredContent(Map.of("updateCount", n)) - .addTextContent("{\"updateCount\":" + n + "}") - .build(); - } - } catch (Exception ex) { - return errorResult(tc.name, ex); - } - }) - .build() - ); - } - List specs = OracleJDBCLogAnalyzerMCPServer.getLogAnalyzerTools(); - for (McpServerFeatures.SyncToolSpecification spec : specs) { - server.addTool(spec); - } - } - } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java index bdeb0fbe..71e87498 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java @@ -7,20 +7,15 @@ package com.oracle.database.jdbc; -import com.fasterxml.jackson.databind.ObjectMapper; import com.oracle.database.jdbc.logs.model.JDBCConnectionEvent; import com.oracle.database.jdbc.logs.model.JDBCExecutedQuery; import com.oracle.database.jdbc.logs.model.LogError; import com.oracle.database.jdbc.logs.model.RDBMSError; import com.oracle.database.jdbc.logs.model.RDBMSPacketDump; -import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; -import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.Tool; -import io.modelcontextprotocol.spec.McpSchema.CallToolResult; -import io.modelcontextprotocol.spec.McpSchema.TextContent; import com.oracle.database.jdbc.logs.analyzer.JDBCLog; import com.oracle.database.jdbc.logs.analyzer.RDBMSLog; @@ -73,10 +68,13 @@ private static SyncToolSpecification getStatsTool() { .description("Return aggregated stats (error count, packets, bytes) from an Oracle JDBC thin log.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall( () -> { + .callHandler((exchange, callReq) -> Utils.tryCall( () -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var stats = new JDBCLog(filePath).getStats(); - return stats.toJSONString(); + return McpSchema.CallToolResult.builder() + .addTextContent(stats.toJSONString()) + .isError(false) + .build(); })) .build(); } @@ -98,12 +96,16 @@ private static SyncToolSpecification getQueriesTool() { .description("Get all executed queries from an Oracle JDBC thin log file, including the timestamp and execution time.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { + .callHandler((exchange, callReq) -> Utils.tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var queries = new JDBCLog(filePath).getQueries(); - return queries.stream() + String results = queries.stream() .map(JDBCExecutedQuery::toJSONString) .collect(Collectors.joining(",", "[", "]")); + return McpSchema.CallToolResult.builder() + .addTextContent(results) + .isError(false) + .build(); })) .build(); } @@ -126,12 +128,16 @@ private static SyncToolSpecification getErrorsTool() { .description("Get all reported errors from an Oracle JDBC thin log file, including stacktrace and log context.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { + .callHandler((exchange, callReq) -> Utils.tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var errors = new JDBCLog(filePath).getLogErrors(); - return errors.stream() + String results = errors.stream() .map(LogError::toJSONString) .collect(Collectors.joining(",", "[", "]")); + return McpSchema.CallToolResult.builder() + .addTextContent(results) + .isError(false) + .build(); })) .build(); } @@ -153,17 +159,22 @@ private static SyncToolSpecification getListLogsDirectoryTool() { .description("List all visible files from a specified directory, which helps the user analyze multiple files with one prompt.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { + .callHandler((exchange, callReq) -> Utils.tryCall(() -> { final var directoryPath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var directory = new File(directoryPath); final var files = directory.listFiles(); if (files == null || files.length == 0) { throw new IOException("No files found in the specified directory."); } - return Arrays.stream(files) - .filter(file -> !file.isHidden() && file.isFile()) - .map(File::getName) - .collect(Collectors.joining(",", "[", "]")); + String results =Arrays.stream(files) + .filter(file -> !file.isHidden() && file.isFile()) + .map(File::getName) + .collect(Collectors.joining(",", "[", "]")); + + return McpSchema.CallToolResult.builder() + .addTextContent(results) + .isError(false) + .build(); })) .build(); } @@ -185,11 +196,14 @@ private static SyncToolSpecification logComparisonTool() { .description("Compare two JDBC log files for performance metrics, errors, and network information.") .inputSchema(ToolSchemas.FILE_COMPARISON_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { + .callHandler((exchange, callReq) -> Utils.tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var secondFilePath = String.valueOf(callReq.arguments().get(SECOND_FILE_PATH)); final var comparison = new JDBCLog(filePath).compareTo(secondFilePath); - return comparison.toJSONString(); + return McpSchema.CallToolResult.builder() + .addTextContent(comparison.toJSONString()) + .isError(false) + .build(); })) .build(); } @@ -209,12 +223,16 @@ private static SyncToolSpecification getConnectionEventsTool() { .description("Retrieve opened and closed JDBC connection events from the log file with timestamp and connection details.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { + .callHandler((exchange, callReq) -> Utils.tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var events = new JDBCLog(filePath).getConnectionEvents(); - return events.stream() + String results = events.stream() .map(JDBCConnectionEvent::toJSONString) .collect(Collectors.joining(",", "[", "]")); + return McpSchema.CallToolResult.builder() + .addTextContent(results) + .isError(false) + .build(); })) .build(); } @@ -237,12 +255,16 @@ private static SyncToolSpecification getRdbmsErrorsTool() { .description("Retrieve errors from an RDBMS/SQLNet trace file.") .inputSchema(ToolSchemas.RDBMS_TOOLS_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { + .callHandler((exchange, callReq) -> Utils.tryCall(() -> { final var logFile = String.valueOf(callReq.arguments().get(FILE_PATH)); final var errors = new RDBMSLog(logFile).getErrors(); - return errors.stream() + String results = errors.stream() .map(RDBMSError::toJSONString) .collect(Collectors.joining(",", "[", "]")); + return McpSchema.CallToolResult.builder() + .addTextContent(results) + .isError(false) + .build(); })) .build(); } @@ -264,13 +286,17 @@ private static SyncToolSpecification getPacketDumpsTool() { .description("Extract packet dumps from RDBMS/SQLNet trace file for given connection ID.") .inputSchema(ToolSchemas.RDBMS_TOOLS_SCHEMA) .build()) - .callHandler((exchange, callReq) -> tryCall(() -> { + .callHandler((exchange, callReq) -> Utils.tryCall(() -> { final var filePath = String.valueOf(callReq.arguments().get(FILE_PATH)); final var connId = String.valueOf(callReq.arguments().get(CONNECTION_ID)); final var packetDumps = new RDBMSLog(filePath).getPacketDumps(connId); - return packetDumps.stream() + String results = packetDumps.stream() .map(RDBMSPacketDump::toJSONString) .collect(Collectors.joining(",", "[", "]")); + return McpSchema.CallToolResult.builder() + .addTextContent(results) + .isError(false) + .build(); })) .build(); } @@ -283,7 +309,7 @@ private static SyncToolSpecification getPacketDumpsTool() { * @param the type of results supplied by this supplier */ @FunctionalInterface - private interface ThrowingSupplier { + interface ThrowingSupplier { /** * Gets a result, potentially throwing an {@link IOException}. * @@ -294,36 +320,4 @@ private interface ThrowingSupplier { } - /** - *

- * Executes the given {@link ThrowingSupplier ThrowingSupplier} action that may throw an {@link IOException} - * and wraps the result in a {@link CallToolResult CallToolResult}. If the action completes successfully, - * the result is returned in a {@link CallToolResult CallToolResult} with {@code isError} set to {@code false}. - * If an {@link IOException} is thrown, the exception message is returned as {@link TextContent TextContent} - * and {@code isError} is set to {@code true}. - *

- * - *

- * This utility method is used to standardize error handling and result formatting for - * methods that perform I/O operations and return responses to the MCP server. - *

- * - * @param action The supplier action to execute, which may throw an {@link IOException}. - * @return a {@link CallToolResult} containing the response and error flag indicating success or failure. - */ - private static CallToolResult tryCall(ThrowingSupplier action) { - boolean isError = false; - String response; - try { - response = action.get(); - } catch (IOException e) { - response = e.getMessage(); - isError = true; - } - - return CallToolResult.builder() - .addTextContent(response) - .isError(isError) - .build(); - } } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 8da93198..d2b25fb4 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -1,6 +1,14 @@ package com.oracle.database.jdbc; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.oracle.database.jdbc.config.ConfigRoot; +import com.oracle.database.jdbc.config.SourceConfig; +import com.oracle.database.jdbc.config.ToolConfig; +import com.oracle.database.jdbc.config.ToolParameterConfig; +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.spec.McpSchema; +import org.yaml.snakeyaml.Yaml; import java.io.IOException; import java.io.Reader; @@ -12,6 +20,9 @@ import java.security.Provider; import java.security.Security; import java.sql.Clob; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -20,16 +31,140 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.stream.Stream; public class Utils { - static McpSchema.CallToolResult errorResult(String toolName, Exception e) { - String msg = e.getMessage() != null ? e.getMessage() : e.toString(); - return McpSchema.CallToolResult.builder() - .isError(true) - .addTextContent("Error in " + toolName + ": " + msg) - .build(); + /** + *

+ * Returns the list of all available tools for this server. + *

+ */ + static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) { + List specs = OracleJDBCLogAnalyzerMCPServer.getLogAnalyzerTools(); + for (McpServerFeatures.SyncToolSpecification spec : specs) { + server.addTool(spec); + } + + // ---------- Dynamically Added Tools ---------- + for (Map.Entry entry : config.tools.entrySet()) { + ToolConfig tc = entry.getValue(); + server.addTool( + McpServerFeatures.SyncToolSpecification.builder() + .tool(McpSchema.Tool.builder() + .name(tc.name) + .title(tc.name) + .description(tc.description) + .inputSchema(ToolSchemas.SQL_ONLY) + .build() + ) + .callHandler((exchange, callReq) -> + Utils.tryCall(() -> { + // Resolve source + SourceConfig src = config.sources.get(tc.source); + String jdbcUrl = (src != null) ? src.toJdbcUrl() : config.dbUrl; + String dbUser = (src != null) ? src.user : config.dbUser; + String dbPassword = (src != null) ? src.password : config.dbPassword; + try (Connection c = DriverManager.getConnection(jdbcUrl, dbUser, dbPassword)) { + PreparedStatement ps = c.prepareStatement(tc.statement); + int paramIdx = 1; + if (tc.parameters != null) { + for (ToolParameterConfig param : tc.parameters) { + Object argVal = callReq.arguments().get(param.name); + ps.setObject(paramIdx++, argVal); + } + } + if (tc.statement.trim().toLowerCase().startsWith("select")) { + ResultSet rs = ps.executeQuery(); + List> rows = rsToList(rs); + return McpSchema.CallToolResult.builder() + .structuredContent(Map.of("rows", rows, "rowCount", rows.size())) + .addTextContent(new ObjectMapper().writeValueAsString(rows)) + .build(); + } else { + int n = ps.executeUpdate(); + return McpSchema.CallToolResult.builder() + .structuredContent(Map.of("updateCount", n)) + .addTextContent("{\"updateCount\":" + n + "}") + .build(); + } + } + }) + ) + .build() + ); + } + } + + /** + * Loads the server configuration from a YAML file specified by the configFile system property. + * If the file cannot be read or parsed, falls back to using only system properties. + * Also initializes tool names for dynamic tool entries. + * + * @return the loaded and initialized {@link ServerConfig} instance. + */ + static ServerConfig loadConfig() { + ServerConfig config; + String configFilePath = System.getProperty("configFile"); + ConfigRoot yamlConfig = null; + try { + try (Reader reader = Files.newBufferedReader(Paths.get(configFilePath))) { + Yaml yaml = new Yaml(); + yamlConfig = yaml.loadAs(reader, ConfigRoot.class); + } + } catch (Exception e) { + Logger logger = Logger.getLogger(OracleDBToolboxMCPServer.class.getName()); + logger.log(Level.SEVERE, e.getMessage()); + } + if (yamlConfig == null) { + config = ServerConfig.fromSystemProperties(); + } else { + String defaultSourceKey = yamlConfig.sources.keySet().stream().findFirst().orElseThrow(); + config = ServerConfig.fromSystemPropertiesAndYaml(yamlConfig, defaultSourceKey); + if (config.tools != null) { + for (Map.Entry entry : config.tools.entrySet()) { + entry.getValue().name = entry.getKey(); + } + } + } + return config; + } + + + /** + *

+ * Executes the provided {@link OracleJDBCLogAnalyzerMCPServer.ThrowingSupplier ThrowingSupplier} action, + * which may throw an {@link Exception}, and returns the resulting {@link McpSchema.CallToolResult}. + *
+ * If the action executes successfully, its {@link McpSchema.CallToolResult} is returned as-is. + * If any exception is thrown, this method returns a {@link McpSchema.CallToolResult} + * with the exception message added as {@link McpSchema.TextContent} and {@code isError} set to {@code true}. + *

+ * + *

+ * This utility method provides standardized error handling and result formatting for methods that may throw exceptions, + * ensuring that errors are consistently reported back to the MCP server. + *

+ * + * @param action The supplier action to execute, which may throw an {@link Exception} and returns a {@link McpSchema.CallToolResult}. + * @return The result of the supplier if successful, or an error {@link McpSchema.CallToolResult} if an exception occurs. + */ + static McpSchema.CallToolResult tryCall(ThrowingSupplier action) { + try { + return action.get(); + } catch (Exception e) { + return McpSchema.CallToolResult.builder() + .addTextContent("Unexpected: " + e.getMessage()) + .isError(true) + .build(); + } + } + + @FunctionalInterface + public interface ThrowingSupplier { + T get() throws Exception; } /** From 340f3f82f2bdfe4e66668db7fcd1ad517017b4cf Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Mon, 24 Nov 2025 14:40:34 +0100 Subject: [PATCH 12/77] Add http streamable transport method && update readme && enable tools system property --- src/oracle-db-toolbox-mcp-server/README.md | 186 ++++++++++++++++-- src/oracle-db-toolbox-mcp-server/pom.xml | 16 ++ .../jdbc/OracleDBToolboxMCPServer.java | 84 +++++++- .../java/com/oracle/database/jdbc/Utils.java | 14 +- 4 files changed, 275 insertions(+), 25 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index ddfcb845..07e03c99 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -2,10 +2,16 @@ ## Overview -The `oracle-db-toolbox-mcp-server` provides the capability to build your own custom tools along with 8 tools for analyzing Oracle JDBC thin client logs and RDBMS/SQLNet trace files: +`oracle-db-toolbox-mcp-server` is a Model Context Protocol (MCP) server that lets you: + * Use 8 built-in tools to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. + * Define your own custom tools via a simple YAML configuration file (optional). + +## Built-in Tools ### Oracle JDBC Log Analysis: +These tools operate on Oracle JDBC thin client logs: + - **`get-stats`**: Extracts performance statistics including error counts, sent/received packets and byte counts. - **`get-queries`**: Retrieves all executed SQL queries with timestamps and execution times. - **`get-errors`**: Extracts all errors reported by both server and client. @@ -15,39 +21,102 @@ The `oracle-db-toolbox-mcp-server` provides the capability to build your own cus ### RDBMS/SQLNet Trace Analysis: +These tools operate on RDBMS/SQLNet trace files: + - **`get-rdbms-errors`**: Extracts errors from RDBMS/SQLNet trace files. - **`get-packet-dumps`**: Extracts packet dumps for a specific connection ID. -## Requirements - -Requirements to build the project: - -- JDK 17 (or higher) -- Maven 3.9 (or higher) +## Prerequisites -## How to use +- **Java 17+** (JDK) +- **Credentials** with permissions for your intended operations +- **MCP client** (e.g., Claude Desktop) to call the tools -### 1- Build the MCP server jar +### Build the MCP server jar ```bash mvn clean install ``` -The created jar can be found in `target/ojdbc-log-analyzer-mcp-server-1.0.0.jar`. +The created jar can be found in `target/oracle-db-toolbox-mcp-server-1.0.0.jar`. -### 2- Configuration +### Transport modes (stdio vs HTTP) -Add (or merge) the following JSON to the configuration file to an MCP client (such as Claude or Cline): +`oracle-db-toolbox-mcp-server` supports two transport modes: -```json +- **Stdio (default)** – the MCP client spawns the JVM process and talks over stdin/stdout +- **HTTP (streamable)** – the MCP server runs as an HTTP service, and clients connect via a URL + +#### Stdio mode (default) + +This is the mode used by tools like Claude Desktop, where the client directly launches: + +```jsonc { "mcpServers": { - "ojdbc-log-analyzer-mcp-server": { - "type": "stdio", + "oracle-db-toolbox-mcp-server": { "command": "java", "args": [ + "-Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service", + "-Ddb.user=your_user", + "-Ddb.password=your_password" + "-Dtools=get-stats,get-queries", + "-Dojdbc.ext.dir=/path/to/extra-jars", "-jar", - "/src/ojdbc-log-analyzer-mcp-server/target/ojdbc-log-analyzer-mcp-server-1.0.0.jar" + "/oracle-db-toolbox-mcp-server-1.0.0.jar" + ] + } + } +} +``` +If you don’t set `-Dtransport`, the server runs in stdio mode by default. + +#### HTTP mode + +In HTTP mode, you run the server as a standalone HTTP service and point an MCP client to it. + +Start the server: + +```shell +java \ + -Dtransport=http \ + -Dhttp.port=45450 \ + -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ + -Ddb.user=your_user \ + -Ddb.password=your_password \ + -Dtools=get-stats,get-queries \ + -jar /oracle-db-toolbox-mcp-server-1.0.0.jar +``` +This exposes the MCP endpoint at: `http://localhost:45450/mcp`. + +### Using HTTP from Cline +Cline supports streamable HTTP directly. Example: + +```json +{ + "mcpServers": { + "oracle-db-toolbox-mcp-server": { + "type": "streamableHttp", + "url": "http://localhost:45450/mcp" + } + } +} +``` + +### Using HTTP from Claude Desktop +Claude Desktop accepts HTTPS endpoints for remote MCP servers. +If your MCP server is only available over plain HTTP (e.g. http://localhost:45450/mcp), +you can use the `mcp-remote` workaround: + +```json +{ + "mcpServers": { + "oracle-db-toolbox-mcp-server": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "http://localhost:45450/mcp" ] } } @@ -78,4 +147,87 @@ tools: To enable YAML configuration, launch the server with: ```bash java -DconfigFile=/path/to/config.yaml -jar .jar -``` \ No newline at end of file +``` + +### Supported System Properties + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyRequiredDescriptionExample
db.urlNo*JDBC URL for Oracle Database. Required only if any database tools are enabled (not required for log-analyzer–only setups).jdbc:oracle:thin:@your-host:1521/your-service
db.userNo*Database username (not required if using token-based auth or centralized config loaded via ojdbc.ext.dir)ADMIN or your-username
db.passwordNo*Database password (not required if using token-based auth or centralized config loaded via ojdbc.ext.dir)your-secure-password
tools (aka -Dtools)No + Comma-separated allow-list of tool names to enable. + Use * or all to enable everything. + If omitted, all tools are enabled by default. + get-stats,get-queries
ojdbc.ext.dirNo + Directory to load extra jars at runtime (keeps the MCP jar lean). + Useful for optional components like oraclepki when using TCPS wallets, token authentication, or centralized driver config. + /opt/oracle/ext-jars
transportNo + Transport mode for the MCP server. Supported values: + stdio or http. If omitted, stdio is used. + http
http.portNo + TCP port used when -Dtransport=http is set. + 45450
configFileNoPath to a YAML file defining `sources` and `tools`./opt/mcp/config.yaml
+ +* Note: If you don’t set tools, all tools are available by default. + +* Conditional requirement: db.url is required **only if** any database tool is enabled via -Dtools. + +If you enable **only** the Log Analyzer tools, you can omit db.url. + +* Note: If you’re using token-based authentication (e.g., IAM tokens) or a centralized configuration provided via the JARs you place in `-Dojdbc.ext.dir`, +you can omit `db.user` and `db.password`. The driver will pick up credentials and security settings from those extensions. diff --git a/src/oracle-db-toolbox-mcp-server/pom.xml b/src/oracle-db-toolbox-mcp-server/pom.xml index f47b4921..432dfaff 100644 --- a/src/oracle-db-toolbox-mcp-server/pom.xml +++ b/src/oracle-db-toolbox-mcp-server/pom.xml @@ -33,6 +33,7 @@ 23.9.0.25.07 2.5 1.0.0 + 12.1.4 5.10.0 ${java.version} ${java.version} @@ -59,12 +60,27 @@ ${snakeYaml.version}
+ com.oracle.database.jdbc ojdbc-log-analyzer ${logAnalyzer.version} + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + ${jetty.version} + + org.junit.jupiter junit-jupiter diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 7ce79764..afa56491 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -3,8 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpSyncServer; +import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.thread.QueuedThreadPool; import static com.oracle.database.jdbc.Utils.installExternalExtensionsFromDir; @@ -19,13 +25,30 @@ public class OracleDBToolboxMCPServer { public static void main(String[] args) { installExternalExtensionsFromDir(); - McpSyncServer server = McpServer.sync(new StdioServerTransportProvider(new ObjectMapper())) - .serverInfo("ojdbc-log-analyzer-mcp-server", "1.0.0") - .capabilities(McpSchema.ServerCapabilities.builder() - .tools(true) - .logging() - .build()) - .build(); + final String transportKind = System.getProperty("transport", "stdio") + .trim() + .toLowerCase(); + + McpSyncServer server; + + switch (transportKind) { + case "http" -> { + server = startHttpServer(); + } + case "stdio" -> { + server = McpServer + .sync(new StdioServerTransportProvider(new ObjectMapper())) + .serverInfo("oracle-db-toolbox-mcp-server", "1.0.0") + .capabilities(McpSchema.ServerCapabilities.builder() + .tools(true) + .logging() + .build()) + .immediateExecution(true) + .build(); + } + default -> throw new IllegalArgumentException( + "Unsupported transport: " + transportKind + " (expected 'stdio' or 'http')"); + } Utils.addSyncToolSpecifications(server, config); } @@ -33,4 +56,51 @@ private OracleDBToolboxMCPServer() { // Prevent instantiation } + /** + * Start HTTP Streamable MCP transport on /mcp using Jetty. + */ + private static McpSyncServer startHttpServer() { + try { + int port = Integer.parseInt(System.getProperty("http.port", "45450")); + + HttpServletStreamableServerTransportProvider transport = + HttpServletStreamableServerTransportProvider.builder() + .objectMapper(new ObjectMapper()) + .mcpEndpoint("/mcp") + .build(); + + McpSyncServer server = McpServer + .sync(transport) + .serverInfo("oracle-db-toolbox-mcp-server", "1.0.0") + .capabilities(McpSchema.ServerCapabilities.builder() + .tools(true) + .logging() + .build()) + .immediateExecution(true) + .build(); + + var threadPool = new QueuedThreadPool(); + threadPool.setName("oracle-db-toolbox-mcp-server"); + var jetty = new Server(threadPool); + + var connector = new ServerConnector(jetty); + connector.setPort(port); + jetty.addConnector(connector); + + var context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(new ServletHolder(transport), "/mcp/*"); + jetty.setHandler(context); + + jetty.start(); + + System.out.println("[oracle-db-toolbox-mcp-server] HTTP transport " + + "started on port " + port + " (endpoint: /mcp)"); + + return server; + } catch (Exception e) { + throw new RuntimeException("Failed to start HTTP/streamable server", e); + } + } + } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index d2b25fb4..5c91ee8b 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -45,7 +45,10 @@ public class Utils { static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) { List specs = OracleJDBCLogAnalyzerMCPServer.getLogAnalyzerTools(); for (McpServerFeatures.SyncToolSpecification spec : specs) { - server.addTool(spec); + String toolName = spec.tool().name(); // e.g. "get-stats", "get-queries" + if (isToolEnabled(config, toolName)) { + server.addTool(spec); + } } // ---------- Dynamically Added Tools ---------- @@ -274,4 +277,13 @@ private static String clobToString(Clob clob) throws SQLException { } return sb.toString(); } + + private static boolean isToolEnabled(ServerConfig config, String toolName) { + if (config.toolsFilter == null) { + return true; + } + String key = toolName.toLowerCase(Locale.ROOT); + return config.toolsFilter.contains(key); + } + } From de8196aa6875d249948815149c2fc8625d2631bc Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Mon, 24 Nov 2025 17:27:56 +0100 Subject: [PATCH 13/77] Adding similarity search and explain/execute plan tools support. Adding UCP. --- src/oracle-db-toolbox-mcp-server/README.md | 51 +++- src/oracle-db-toolbox-mcp-server/pom.xml | 5 + .../jdbc/ExplainAndExecutePlanTool.java | 276 ++++++++++++++++++ .../jdbc/OracleDBToolboxMCPServer.java | 14 + .../database/jdbc/SimilaritySearchTool.java | 147 ++++++++++ .../com/oracle/database/jdbc/ToolSchemas.java | 50 ++++ .../java/com/oracle/database/jdbc/Utils.java | 107 ++++++- 7 files changed, 646 insertions(+), 4 deletions(-) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 07e03c99..7613bfb5 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -4,7 +4,8 @@ `oracle-db-toolbox-mcp-server` is a Model Context Protocol (MCP) server that lets you: * Use 8 built-in tools to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. - * Define your own custom tools via a simple YAML configuration file (optional). + * Optionally use **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**, when JDBC configuration is provided. + * Define your own custom tools via a simple YAML configuration file. ## Built-in Tools @@ -26,12 +27,58 @@ These tools operate on RDBMS/SQLNet trace files: - **`get-rdbms-errors`**: Extracts errors from RDBMS/SQLNet trace files. - **`get-packet-dumps`**: Extracts packet dumps for a specific connection ID. +### Vector Similarity Search + +* **`similarity_search`**: Perform semantic similarity search using Oracle’s vector features (`VECTOR_EMBEDDING`, `VECTOR_DISTANCE`). + + **Inputs:** + + * `question` (string, required): Natural language query. + * `topK` (integer, optional, default: 5): Number of closest results. + * `table` (string, default: `profile_oracle`): Table containing text + vector embeddings. + * `dataColumn` (string, default: `text`): Text/CLOB column. + * `embeddingColumn` (string, default: `embedding`): Vector column. + * `modelName` (string, default: `doc_model`): Name of the DB vector model. + * `textFetchLimit` (integer, default: 4000): Max length of returned text. + + **Returns:** + + * JSON array of similar rows with scores and truncated snippets. + +### SQL Execution Plan Analysis + +* **`explain_plan`**: Generate Oracle execution plans and receive a pre-formatted LLM prompt for tuning and explanation. + + **Modes:** + + * `static` — Uses `EXPLAIN PLAN` (estimated plan; does not run the SQL). + * `dynamic` — Uses `DBMS_XPLAN.DISPLAY_CURSOR` for the **actual** plan of a cursor. + + **Inputs:** + + * `sql` (required): SQL query to analyze. + * `mode` (static|dynamic, default: static) + * `execute` (boolean): Execute SQL to obtain a cursor in dynamic mode. + * `maxRows` (integer, default: 1): Limit rows fetched during execution. + * `xplanOptions` (string): Formatting options. + + * Default dynamic: `ALLSTATS LAST +PEEKED_BINDS +OUTLINE +PROJECTION` + * Default static: `BASIC +OUTLINE +PROJECTION +ALIAS` + + **Returns:** + + * `planText`: DBMS_XPLAN output. + * `llmPrompt`: A structured prompt for an LLM to explain + tune the plan. + +--- ## Prerequisites - **Java 17+** (JDK) - **Credentials** with permissions for your intended operations - **MCP client** (e.g., Claude Desktop) to call the tools +> The server uses UCP pooling out of the box (initial/min= 1). + ### Build the MCP server jar ```bash @@ -133,7 +180,7 @@ sources: prod-db: url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 user: ADMIN - password: mypassword + password: ${password} tools: hotels-by-name: diff --git a/src/oracle-db-toolbox-mcp-server/pom.xml b/src/oracle-db-toolbox-mcp-server/pom.xml index 432dfaff..25e482dd 100644 --- a/src/oracle-db-toolbox-mcp-server/pom.xml +++ b/src/oracle-db-toolbox-mcp-server/pom.xml @@ -53,6 +53,11 @@ ojdbc17 ${ojdbc.version} + + com.oracle.database.jdbc + ucp17 + ${ojdbc.version} + org.yaml diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java new file mode 100644 index 00000000..253e393b --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java @@ -0,0 +1,276 @@ +package com.oracle.database.jdbc; + +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.spec.McpSchema; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.oracle.database.jdbc.Utils.openConnection; +import static com.oracle.database.jdbc.Utils.tryCall; + +public class ExplainAndExecutePlanTool { + static McpServerFeatures.SyncToolSpecification getExplainAndExecutePlanTool(ServerConfig config) { + return + McpServerFeatures.SyncToolSpecification.builder() + .tool(McpSchema.Tool.builder() + .name("explain_plan") + .title("Explain Plan (static or dynamic)") + .description(""" + Returns an Oracle execution plan for the provided SQL. + mode: "static" (EXPLAIN PLAN) or "dynamic" (DISPLAY_CURSOR of the last execution in this session). + Response includes: planText (DBMS_XPLAN output) and llmPrompt (ready-to-use for the LLM). + + You are an Oracle SQL performance expert. Explain the execution plan to the user in clear language and then provide prioritized, practical tuning advice. + + Instructions: + 1) Summarize how the query executes (major steps, joins, access paths). + 2) Point out potential bottlenecks (scans, sorts, joins, TEMP/PGA, cardinality mismatches if present). + 3) Give the top 3–5 tuning ideas with rationale (indexes, predicates, rewrites, stats/histograms, hints if appropriate). + 4) Mention any trade-offs or risks. + + Note to model: + If the sql is a dml operation and it was actually executed No permanent data changes were committed. When explaining the plan, mention this statement will be rolled back + """) + .inputSchema(ToolSchemas.EXPLAIN_PLAN) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + try (Connection c = openConnection(config)) { + final String sql = String.valueOf(callReq.arguments().get("sql")); + if (sql == null || sql.isBlank()) { + return new McpSchema.CallToolResult("Parameter 'sql' is required", true); + } + final String mode = String.valueOf(callReq.arguments().getOrDefault("mode", "static")) + .toLowerCase(Locale.ROOT); + + Boolean executeArg = null; + Object exObj = callReq.arguments().get("execute"); + if (exObj != null) executeArg = Boolean.parseBoolean(String.valueOf(exObj)); + + Integer maxRows = null; + try { + Object mr = callReq.arguments().get("maxRows"); + if (mr != null) maxRows = Integer.parseInt(String.valueOf(mr)); + } catch (Exception ignored) {} + + final String xplanOptions = Optional.ofNullable(callReq.arguments().get("xplanOptions")) + .map(Object::toString).orElse(null); + + var res = getExplainPlan( + c, + sql, + "dynamic".equals(mode), + maxRows, + executeArg, + xplanOptions + ); + Map payload = new LinkedHashMap<>(); + payload.put("mode", mode); + payload.put("sql", sql); + payload.put("planText", res.planText()); + + return McpSchema.CallToolResult.builder() + .structuredContent(payload) + .addTextContent(res.planText()) + .build(); + } + })) + .build(); + } + + + /** + * Returns an execution plan (static or dynamic) for the given SQL and also produces + * an accompanying LLM prompt to explain and tune the plan. + * - static → EXPLAIN PLAN (no execution, estimated plan only) + * - dynamic → DISPLAY_CURSOR (requires a real cursor; may lightly execute the SQL) + * + * @param c JDBC connection + * @param sql SQL to analyze + * @param dynamic true = dynamic plan, false = static plan + * @param maxRows limit when lightly executing SELECT (default = 1) + * @param execute whether to execute or just parse (null = auto per SQL type) + * @param xplanOptions DBMS_XPLAN formatting options + */ + + static ExplainResult getExplainPlan( + Connection c, + String sql, + boolean dynamic, + Integer maxRows, + Boolean execute, + String xplanOptions + ) throws Exception { + + if (!dynamic) { + try (Statement st = c.createStatement()) { + st.executeUpdate("EXPLAIN PLAN FOR " + sql); + } + String planText = readXplan(c, false, xplanOptions); + return new ExplainResult(planText); + } + + // dynamic mode → prepare or execute depending on flags + runQueryLightweight(c, sql, maxRows, execute); + + String planText = readXplan(c, true, xplanOptions); + return new ExplainResult(planText); + } + + + /** + * Prepare or execute a statement lightly so a cursor exists for DISPLAY_CURSOR. + * Handles SELECT, DML, and DDL safely. + * + * @param c open connection + * @param sql SQL text + * @param maxRows optional limit (applies to SELECT only) + * @param execute whether to actually execute (null = smart default) + */ + private static void runQueryLightweight(Connection c, String sql, Integer maxRows, Boolean execute) + throws SQLException { + + boolean isSelect = looksSelect(sql); + boolean doExecute = (execute != null) ? execute : isSelect; // smart default + + if (!doExecute) { + // just parse (safe) + try (PreparedStatement ps = c.prepareStatement(sql)) { /* parse only */ } + return; + } + + if (isSelect) { + try (PreparedStatement ps = c.prepareStatement(sql)) { + ps.setMaxRows((maxRows != null && maxRows > 0) ? maxRows : 1); + ps.setFetchSize(1); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { /* first row only */ } + } + } + return; + } + + // DML/DDL: execute inside rollback-safe transaction (to minimise side effects) + boolean prevAutoCommit = c.getAutoCommit(); + try { + c.setAutoCommit(false); + + String execSql = injectGatherStatsHintAfterVerb(sql); + try (PreparedStatement ps = c.prepareStatement(execSql)) { + ps.execute(); + } + + c.rollback(); + } finally { + c.setAutoCommit(prevAutoCommit); + } + + } + + /** Read DBMS_XPLAN for either EXPLAIN PLAN or last cursor. */ + private static String readXplan(Connection c, boolean dynamic, String xplanOptions) throws SQLException { + final String opts = (xplanOptions == null || xplanOptions.isBlank()) + ? (dynamic ? "ALLSTATS LAST +PEEKED_BINDS +OUTLINE +PROJECTION" + : "BASIC +OUTLINE +PROJECTION +ALIAS") + : xplanOptions; + final String q = dynamic + ? ("SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, '" + opts + "'))") + : ("SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, NULL, '" + opts + "'))"); + + StringBuilder sb = new StringBuilder(); + try (Statement st = c.createStatement(); ResultSet rs = st.executeQuery(q)) { + while (rs.next()) sb.append(Objects.toString(rs.getString(1), "")).append('\n'); + } + return sb.toString().trim(); + } + + /** + * Checks if the provided SQL looks like a SELECT. + * + * @param sql the SQL string + * @return true if it begins with "SELECT" (case-insensitive) + */ + static boolean looksSelect(String sql) { + return sql != null && sql.trim().regionMatches(true, 0, "SELECT", 0, 6); + } + + private static final Pattern DML_VERB = + Pattern.compile("^\\s*(?:--.*?$|/\\*.*?\\*/\\s*)*(UPDATE|DELETE|INSERT|MERGE)\\b", + Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); + + /** Injects "/*+ gather_plan_statistics +*\/" immediately after the first DML verb. + * - Preserves leading whitespace and comments + * - No-op if the SQL already contains the hint (case-insensitive) + * - Skips if not a DML statement (e.g., SELECT/BEGIN/DECLARE/ALTER/CREATE) + */ + static String injectGatherStatsHintAfterVerb(String sql) { + if (sql == null) return null; + String s = sql.trim(); + if (s.toLowerCase(Locale.ROOT).contains("gather_plan_statistics")) return sql; + String head = s.length() >= 16 ? s.substring(0, 16).toLowerCase(Locale.ROOT) : s.toLowerCase(Locale.ROOT); + if (head.startsWith("begin") || head.startsWith("declare") || isDdl(head)) { + return sql; + } + return injectAfterMatch(sql, DML_VERB, "/*+ gather_plan_statistics */", "gather_plan_statistics"); + } + + static final Pattern FIRST_WORD = Pattern.compile("^\\s*([A-Za-z0-9_]+)"); + + /** + * DDL detector (CREATE/ALTER/DROP/TRUNCATE/RENAME/COMMENT/GRANT/REVOKE). + * Used to block DDL inside user-managed transactions. + */ + static boolean isDdl(String sql) { + if (sql == null) return false; + String s = sql.trim().toUpperCase(); + return s.startsWith("CREATE ") + || s.startsWith("ALTER ") + || s.startsWith("DROP ") + || s.startsWith("TRUNCATE ") + || s.startsWith("RENAME ") + || s.startsWith("COMMENT ") + || s.startsWith("GRANT ") + || s.startsWith("REVOKE "); + } + + record ExplainResult(String planText) {} + + /** + * Injects a given string after the first match group found by the pattern, + * unless the SQL already contains the skip string (case-insensitive). + * + * @param sql SQL statement to operate on + * @param pattern Regex pattern with a capturing group for insertion point + * @param injection Text to inject + * @param skipIfContains Injection is skipped if this substring (case-insensitive) is present + * @return Modified SQL with the injection, or original if no changes made + */ + private static String injectAfterMatch( + String sql, Pattern pattern, String injection, String skipIfContains + ) { + if (sql == null) return null; + String s = sql.trim(); + if (s.toLowerCase(Locale.ROOT).contains(skipIfContains.toLowerCase(Locale.ROOT))) + return sql; + Matcher m = pattern.matcher(sql); + if (!m.find()) return sql; + int start = m.start(1), end = m.end(1); + String word = sql.substring(start, end); + StringBuilder out = new StringBuilder(sql.length() + injection.length() + 4); + out.append(sql, 0, start) + .append(word) + .append(" ").append(injection) + .append(sql.substring(end)); + return out.toString(); + } +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index afa56491..d1e0307d 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -12,6 +12,8 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.util.thread.QueuedThreadPool; +import javax.sql.DataSource; + import static com.oracle.database.jdbc.Utils.installExternalExtensionsFromDir; public class OracleDBToolboxMCPServer { @@ -22,6 +24,18 @@ public class OracleDBToolboxMCPServer { config = Utils.loadConfig(); } + /** + * Injects a custom {@link javax.sql.DataSource} used by all tools + * to obtain connections. + *

Call this before {@link #main(String[])} to override the default + * configuration-based data source.

+ * + * @param ds the data source to use for all DB operations + */ + public static void useDataSource(DataSource ds) { + Utils.useDataSource(ds); + } + public static void main(String[] args) { installExternalExtensionsFromDir(); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java new file mode 100644 index 00000000..3dd36f49 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java @@ -0,0 +1,147 @@ +package com.oracle.database.jdbc; + +import com.fasterxml.jackson.databind.json.JsonMapper; +import io.modelcontextprotocol.server.McpServerFeatures; +import io.modelcontextprotocol.spec.McpSchema; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static com.oracle.database.jdbc.Utils.openConnection; +import static com.oracle.database.jdbc.Utils.tryCall; +import static com.oracle.database.jdbc.Utils.getOrDefault; + +public class SimilaritySearchTool { + + private static final Pattern SAFE_IDENT = Pattern.compile("[A-Za-z0-9_$.#]+"); + + private static final String DEFAULT_VECTOR_TABLE = "profile_oracle"; + private static final String DEFAULT_VECTOR_DATA_COLUMN = "text"; + private static final String DEFAULT_VECTOR_EMBEDDING_COLUMN = "embedding"; + private static final String DEFAULT_VECTOR_MODEL_NAME = "doc_model"; + private static final int DEFAULT_VECTOR_TEXT_FETCH_LIMIT = 4000; + private static final String SIMILARITY_SEARCH = """ + SELECT dbms_lob.substr(%s, %s, 1) AS text + FROM %s + ORDER BY VECTOR_DISTANCE(%s, + TO_VECTOR(VECTOR_EMBEDDING(%s USING ? AS data))) + FETCH FIRST ? ROWS ONLY + """; + + static McpServerFeatures.SyncToolSpecification getSymilaritySearchTool(ServerConfig config) { + + return McpServerFeatures.SyncToolSpecification.builder() + .tool(McpSchema.Tool.builder() + .name("similarity_search") + .title("Similarity Search") + .description("Semantic vector similarity over a table with (text, embedding) columns") + .inputSchema(ToolSchemas.SIMILARITY_SEARCH) + .build()) + .callHandler((exchange, callReq) -> tryCall(() -> { + try (Connection c = openConnection(config)) { + Map arguments = callReq.arguments(); + String question = String.valueOf(arguments.get("question")); + if (question == null || question.isBlank()) { + return new McpSchema.CallToolResult("Question must be non-blank", true); + } + int topK; + try { + topK = Integer.parseInt(String.valueOf(arguments.getOrDefault("topK", 5))); + } catch (NumberFormatException e) { + topK = 5; + } + topK = Math.max(1, Math.min(100, topK)); + + String table = getOrDefault(arguments.get("table"), DEFAULT_VECTOR_TABLE); + String dataColumn = getOrDefault(arguments.get("dataColumn"), DEFAULT_VECTOR_DATA_COLUMN); + String embeddingColumn = getOrDefault(arguments.get("embeddingColumn"), DEFAULT_VECTOR_EMBEDDING_COLUMN); + String modelName = getOrDefault(arguments.get("modelName"), DEFAULT_VECTOR_MODEL_NAME); + + int textFetchLimit = DEFAULT_VECTOR_TEXT_FETCH_LIMIT; + Object limitArg = arguments.get("textFetchLimit"); + if (limitArg != null) { + try { + textFetchLimit = Math.max(1, Integer.parseInt(String.valueOf(limitArg))); + } + catch (NumberFormatException ignored) {} + } + + List results = runSimilaritySearch( + c, table, dataColumn, embeddingColumn, modelName, textFetchLimit, question, topK); + + return McpSchema.CallToolResult.builder() + .structuredContent(Map.of("rows", results)) + .addTextContent(new JsonMapper().writeValueAsString(results)) + .build(); + } + })) + .build(); + } + + + /** + * Executes a vector similarity search against the configured table. + * + *

Uses the columns/table/model declared in {@link ServerConfig} and returns the + * text fragments of the top matches.

+ * + * @param c an open JDBC connection + * @param table table name containing text + embedding columns + * @param dataColumn column holding the text/CLOB to return + * @param embeddingColumn vector column used by the similarity function + * @param modelName database vector model used to embed the question + * @param textFetchLimit substring length to return from the text column + * @param question natural-language query text + * @param topK maximum number of rows to return (clamped by caller) + * @return list of text snippets ranked by similarity + * @throws java.sql.SQLException if the SQL execution fails + */ + private static List runSimilaritySearch(Connection c, + String table, + String dataColumn, + String embeddingColumn, + String modelName, + int textFetchLimit, + String question, + int topK) throws SQLException { + String sql = String.format( + SIMILARITY_SEARCH, + quoteIdent(dataColumn), textFetchLimit, quoteIdent(table), embeddingColumn, modelName + ); + + List result = new ArrayList<>(); + try (PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, question); + ps.setInt(2, topK); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + result.add(rs.getString("text")); + } + } + } + return result; + } + + + /** + * Escapes and quotes a potentially unsafe identifier for SQL use. + * + * @param ident identifier to quote + * @return a quoted or validated identifier + */ + static String quoteIdent(String ident) { + if (ident == null) throw new IllegalArgumentException("identifier is null"); + String s = ident.trim(); + if (!SAFE_IDENT.matcher(s).matches()) { + return "\"" + s.replace("\"", "\"\"") + "\""; + } + return s; + } + +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java index dc2d34a8..712a92d1 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java @@ -61,4 +61,54 @@ public class ToolSchemas { "required": ["filePath", "connectionId"] } """; + static final String SIMILARITY_SEARCH = """ + { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "Natural-language query text" + }, + "topK": { + "type": "integer", + "description": "Number of rows to return", + "default": 5 + }, + "table": { + "type": "string", + "description": "Override: table name" + }, + "dataColumn": { + "type": "string", + "description": "Override: text/CLOB column" + }, + "embeddingColumn": { + "type": "string", + "description": "Override: embedding column" + }, + "modelName": { + "type": "string", + "description": "Override: vector model name" + }, + "textFetchLimit": { + "type": "integer", + "description": "Override: substring length (CLOB)" } + }, + "required": ["question"] + }"""; + static final String EXPLAIN_PLAN = """ + { + "type": "object", + "properties": { + "sql": { "type": "string", "description": "SQL to plan" }, + "mode": { "type": "string", "enum": ["static","dynamic"], "description": "static=EXPLAIN PLAN, dynamic=DISPLAY_CURSOR" }, + "maxRows": { "type": "integer", "minimum": 1, "description": "When executing SELECT in dynamic mode, cap rows fetched" }, + "execute": { "type": "boolean", "description": "If true, actually run the SQL to collect runtime stats (A-Rows). Default: SELECT=true, DML/DDL=false" }, + "xplanOptions": { + "type": "string", + "description": "Override DBMS_XPLAN options, e.g. 'ALLSTATS LAST +PEEKED_BINDS +OUTLINE +PROJECTION'" + } + }, + "required": ["sql"] + }"""; } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 5c91ee8b..4f50b17e 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -9,7 +9,10 @@ import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.spec.McpSchema; import org.yaml.snakeyaml.Yaml; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; +import javax.sql.DataSource; import java.io.IOException; import java.io.Reader; import java.net.URL; @@ -31,12 +34,44 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; +/** + * Utility class for managing Oracle database connections and + * executing SQL operations. + * + *

Provides methods for connection pooling (using Oracle UCP), + * executing queries, converting results to JSON, and safely handling + * database identifiers. + * + *

The connection pool uses minimal settings (1 connection). + * Applications can override the default data source via + * {@link #useDataSource(DataSource)}. + */ public class Utils { + private static volatile DataSource dataSource; + private static volatile Supplier connectionSupplier; + + /** + * Overrides the default data source with a caller-provided one. + * Call before the server starts registering tools. + * + * @param ds custom data source used to obtain connections + */ + static void useDataSource(DataSource ds) { + connectionSupplier = () -> { + try { + return ds.getConnection(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + }; + } + /** *

* Returns the list of all available tools for this server. @@ -51,6 +86,16 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) } } + // similarity_search + if (isToolEnabled(config, "similarity_search")) { + server.addTool(SimilaritySearchTool.getSymilaritySearchTool(config)); + } + + // explain_plan + if (isToolEnabled(config, "explain_plan")) { + server.addTool(ExplainAndExecutePlanTool.getExplainAndExecutePlanTool(config)); + } + // ---------- Dynamically Added Tools ---------- for (Map.Entry entry : config.tools.entrySet()) { ToolConfig tc = entry.getValue(); @@ -64,7 +109,7 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) .build() ) .callHandler((exchange, callReq) -> - Utils.tryCall(() -> { + tryCall(() -> { // Resolve source SourceConfig src = config.sources.get(tc.source); String jdbcUrl = (src != null) ? src.toJdbcUrl() : config.dbUrl; @@ -101,6 +146,12 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) } } + static String getOrDefault(Object v, String def) { + if (v == null) return def; + String s = v.toString().trim(); + return s.isEmpty() ? def : s; + } + /** * Loads the server configuration from a YAML file specified by the configFile system property. * If the file cannot be read or parsed, falls back to using only system properties. @@ -135,6 +186,58 @@ static ServerConfig loadConfig() { return config; } + /** + * Acquires a JDBC connection from the active data source. + * + * @param cfg server configuration + * @return open JDBC connection + * @throws SQLException on acquisition failure + */ + static Connection openConnection(ServerConfig cfg) throws SQLException { + Supplier s = connectionSupplier; + if (s != null) { + try { + return s.get(); + } catch (RuntimeException re) { + if (re.getCause() instanceof SQLException se) throw se; + throw re; + } + } + return getDataSource(cfg).getConnection(); + } + + /** + * Lazily initializes and returns a UCP {@link PoolDataSource} using + * values from {@link ServerConfig}. The pool is kept minimal and + * predictable (initial/min/max = 1). + * + * @throws SQLException if creation or configuration fails + */ + private static DataSource getDataSource(ServerConfig cfg) throws SQLException { + if (dataSource != null) return dataSource; + synchronized (Utils.class) { + if (dataSource != null) + return dataSource; + + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(cfg.dbUrl); + if (cfg.dbUser != null) + pds.setUser(cfg.dbUser); + if (cfg.dbPassword != null) + pds.setPassword(cfg.dbPassword); + + pds.setInitialPoolSize(1); + pds.setMinPoolSize(1); + pds.setConnectionWaitTimeout(10); + pds.setConnectionProperty("remarksReporting", "true"); + pds.setConnectionProperty("oracle.jdbc.vectorDefaultGetObjectType", "double[]"); + pds.setConnectionProperty("oracle.jdbc.jsonDefaultGetObjectType", "java.lang.String"); + + dataSource = pds; + return dataSource; + } + } /** *

@@ -286,4 +389,4 @@ private static boolean isToolEnabled(ServerConfig config, String toolName) { return config.toolsFilter.contains(key); } -} +} \ No newline at end of file From ef02f18c975de5167d698ef430fe80dc01dc3962 Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Tue, 25 Nov 2025 10:32:04 +0100 Subject: [PATCH 14/77] update jetty version to 12.1.1 --- src/oracle-db-toolbox-mcp-server/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/pom.xml b/src/oracle-db-toolbox-mcp-server/pom.xml index 432dfaff..2ef6062d 100644 --- a/src/oracle-db-toolbox-mcp-server/pom.xml +++ b/src/oracle-db-toolbox-mcp-server/pom.xml @@ -33,7 +33,7 @@ 23.9.0.25.07 2.5 1.0.0 - 12.1.4 + 12.1.1 5.10.0 ${java.version} ${java.version} From a3a2147baba9fc5f441b01522498e5537950848a Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 14:39:29 +0100 Subject: [PATCH 15/77] Rename OracleJDBCLogAnalyzerMCPServer class and test to OracleJDBCLogAnalyzer --- ...BCLogAnalyzerMCPServer.java => OracleJDBCLogAnalyzer.java} | 0 ...lyzerMCPServerTest.java => OracleJDBCLogAnalyzerTest.java} | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/{OracleJDBCLogAnalyzerMCPServer.java => OracleJDBCLogAnalyzer.java} (100%) rename src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/{OracleJDBCLogAnalyzerMCPServerTest.java => OracleJDBCLogAnalyzerTest.java} (96%) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java similarity index 100% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServer.java rename to src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java diff --git a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java similarity index 96% rename from src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java rename to src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java index 82411138..b1a1f1f3 100644 --- a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerMCPServerTest.java +++ b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java @@ -14,13 +14,13 @@ import static org.junit.jupiter.api.Assertions.*; @SuppressWarnings("unchecked") -class OracleJDBCLogAnalyzerMCPServerTest { +class OracleJDBCLogAnalyzerTest { private static Map tools; @BeforeAll static void initializeTools(){ - tools = OracleJDBCLogAnalyzerMCPServer.getLogAnalyzerTools() + tools = OracleJDBCLogAnalyzer.getLogAnalyzerTools() .stream() .map(SyncToolSpecification::tool) .collect(Collectors.toMap(McpSchema.Tool::name, identity())); From 1ea3202554ec634f3e76ef4692c0b14f5272f87f Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 14:48:57 +0100 Subject: [PATCH 16/77] Add oauth and web packages --- .../jdbc/OracleDBToolboxMCPServer.java | 49 ++++++- .../jdbc/oauth/OAuth2Configuration.java | 121 ++++++++++++++++++ .../jdbc/oauth/OAuth2TokenValidator.java | 87 +++++++++++++ .../database/jdbc/oauth/TokenGenerator.java | 47 +++++++ .../jdbc/web/AuthorizationFilter.java | 62 +++++++++ .../database/jdbc/web/WellKnownServlet.java | 36 ++++++ 6 files changed, 398 insertions(+), 4 deletions(-) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2TokenValidator.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index d1e0307d..519103d0 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -1,22 +1,29 @@ package com.oracle.database.jdbc; import com.fasterxml.jackson.databind.ObjectMapper; +import com.oracle.database.jdbc.web.AuthorizationFilter; +import com.oracle.database.jdbc.web.WellKnownServlet; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; + +import org.eclipse.jetty.ee10.servlet.FilterHolder; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import javax.sql.DataSource; +import java.util.logging.Logger; + import static com.oracle.database.jdbc.Utils.installExternalExtensionsFromDir; public class OracleDBToolboxMCPServer { + private static final Logger LOG = Logger.getLogger(OracleDBToolboxMCPServer.class.getName()); static ServerConfig config; @@ -104,12 +111,19 @@ private static McpSyncServer startHttpServer() { var context = new ServletContextHandler(); context.setContextPath("/"); context.addServlet(new ServletHolder(transport), "/mcp/*"); + context.addServlet(WellKnownServlet.class.getName(), "/.well-known/oauth-protected-resource"); + + var oauthFilter = new FilterHolder(new AuthorizationFilter()); + context.addFilter(oauthFilter, "/mcp/*", null); + jetty.setHandler(context); jetty.start(); - System.out.println("[oracle-db-toolbox-mcp-server] HTTP transport " + - "started on port " + port + " (endpoint: /mcp)"); + final String url = buildServerURL(jetty); + final String mcpEndpoint = url + "/mcp"; + + LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on %s (endpoint: %s)".formatted(url, mcpEndpoint)); return server; } catch (Exception e) { @@ -117,4 +131,31 @@ private static McpSyncServer startHttpServer() { } } + private static String buildServerURL(Server jetty) { + String host = "localhost"; + String protocol = "http"; + int port = 45450; + + for (final org.eclipse.jetty.server.Connector conn : jetty.getConnectors()) + if (conn instanceof ServerConnector serverConnector) { + if (serverConnector.getHost() != null) { + host = serverConnector.getHost(); + port = serverConnector.getPort(); + } + + for (final org.eclipse.jetty.server.ConnectionFactory factory : serverConnector.getConnectionFactories()) + if (factory instanceof SslContextFactory || factory.getProtocol().toLowerCase().contains("ssl")) { + protocol = "https"; + } + + break; + } + + final var url = "%s://%s:%s".formatted(protocol, host, port); + + System.setProperty("serverURL", url); + + return url; + } + } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java new file mode 100644 index 00000000..772147f9 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java @@ -0,0 +1,121 @@ +package com.oracle.database.jdbc.oauth; + +import java.util.logging.Logger; + +/** + * The OAuth2Configuration class is a singleton that manages OAuth2 authentication configuration settings. + * It reads configuration values from system properties and provides access to them via getter methods. + * This class also handles logging based on whether authentication and OAuth2 are enabled or configured. + * If OAuth2 is not properly configured, it initializes a TokenGenerator for local token generation. + */ +public class OAuth2Configuration { + private static final Logger LOG = Logger.getLogger(OAuth2Configuration.class.getName()); + private static final OAuth2Configuration INSTANCE = new OAuth2Configuration(); + + /** The OAuth2 authorization server URL. */ + private final String authServer; + /** The OAuth2 token introspection endpoint URL. */ + private final String introspectionEndpoint; + /** The OAuth2 client ID. */ + private final String clientId; + /** The OAuth2 client secret. */ + private final String clientSecret; + + /** Flag indicating whether authentication is enabled. */ + private final boolean isAuthenticationEnabled; + /** Flag indicating whether OAuth2 is fully configured. */ + private final boolean isOAuth2Configured; + + /** + * Private constructor to initialize the singleton instance. + * Reads system properties for authentication settings and OAuth2 configuration. + * Logs warnings or info messages based on the configuration status. + * If OAuth2 is not configured, initializes a TokenGenerator for local token generation. + */ + private OAuth2Configuration() { + isAuthenticationEnabled = Boolean.parseBoolean(System.getProperty("enableAuthentication","false")); + authServer = System.getProperty("authServer"); + introspectionEndpoint = System.getProperty("introspectionEndpoint"); + clientId = System.getProperty("clientId"); + clientSecret = System.getProperty("clientSecret"); + isOAuth2Configured = authServer != null && introspectionEndpoint != null && clientId != null && clientSecret != null; + + if (!isAuthenticationEnabled) + LOG.warning("Authentication is disabled"); + else { + LOG.info("Authentication is enabled"); + + if (isOAuth2Configured) + LOG.info("OAuth2 is configured"); + else { + LOG.warning("OAuth2 is not configured"); + // Generate a local UUID string token + TokenGenerator.getInstance(); + } + } + } + + /** + * Returns the singleton instance of OAuth2Configuration. + * + * @return the singleton instance + */ + public static OAuth2Configuration getInstance() { + return INSTANCE; + } + + /** + * Returns the OAuth2 authorization server URL. + * + * @return the auth server URL + */ + public String getAuthServer() { + return authServer; + } + + /** + * Returns the OAuth2 client ID. + * + * @return the client ID + */ + public String getClientId() { + return clientId; + } + + /** + * Returns the OAuth2 client secret. + * + * @return the client secret + */ + public String getClientSecret() { + return clientSecret; + } + + /** + * Returns the OAuth2 token introspection endpoint URL. + * + * @return the introspection endpoint URL + */ + public String getIntrospectionEndpoint() { + return introspectionEndpoint; + } + + /** + * Checks if authentication is enabled. + * + * @return true if authentication is enabled, false otherwise + */ + public boolean isAuthenticationEnabled() { + return isAuthenticationEnabled; + } + + /** + * Checks if OAuth2 is fully configured. + * + * @return true if OAuth2 is configured, false otherwise + */ + public boolean isOAuth2Configured() { + return isOAuth2Configured; + } +} + diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2TokenValidator.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2TokenValidator.java new file mode 100644 index 00000000..57e16766 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2TokenValidator.java @@ -0,0 +1,87 @@ +package com.oracle.database.jdbc.oauth; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.Base64; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The OAuth2TokenValidator class is responsible for validating OAuth2 access tokens. + * It checks if the provided access token is valid by either using a local TokenGenerator + * if OAuth2 is not configured, or by performing token introspection against an OAuth2 + * authorization server if OAuth2 is properly configured. + *

+ * This class relies on the OAuth2Configuration singleton to retrieve necessary settings + * such as the introspection endpoint, client credentials, and configuration flags. + *

+ */ +public class OAuth2TokenValidator { + private static final OAuth2Configuration OAUTH_CONFIG = OAuth2Configuration.getInstance(); + private static final Logger LOG = Logger.getLogger(OAuth2TokenValidator.class.getName()); + + /** + * Validates the given access token. + *

+ * If OAuth2 is not configured (as determined by OAuth2Configuration), this method + * delegates validation to the TokenGenerator instance for local verification. + * Otherwise, it performs an HTTP POST request to the OAuth2 introspection endpoint + * using the configured client credentials. The response is parsed as JSON, and the + * "active" field is checked to determine token validity. + *

+ * + * @param accessToken the OAuth2 access token to validate; must not be null or blank + * @return true if the token is valid, false otherwise + * @throws RuntimeException if an error occurs during token validation (e.g., network issues), + * though exceptions are logged and handled internally by returning false + */ + public boolean isTokenValid(final String accessToken) { + if (!OAUTH_CONFIG.isOAuth2Configured()) + return TokenGenerator.getInstance().verifyToken(accessToken); + + boolean isTokenValid = false; + if (accessToken == null || accessToken.isBlank()) + return false; + + final var clientCredentials = "%s:%s".formatted(OAUTH_CONFIG.getClientId(), OAUTH_CONFIG.getClientSecret()); + final var encodedClientCredentials = Base64.getEncoder() + .encodeToString(clientCredentials.getBytes()); + final var requestBody = "token=" + accessToken; + + try { + final HttpClient client = HttpClient.newHttpClient(); + final HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(OAUTH_CONFIG.getIntrospectionEndpoint())) + .header("Authorization", "Basic " + encodedClientCredentials) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(HttpRequest.BodyPublishers.ofString(requestBody)) + .build(); + + final HttpResponse response = client.send(request, BodyHandlers.ofString()); + + final int statusCode = response.statusCode(); + if (statusCode == HttpServletResponse.SC_OK) { + final var mapper = new ObjectMapper(); + final var jsonNode = mapper.readTree(response.body()); + + isTokenValid = jsonNode.get("active").asBoolean(); + } + } catch (IOException | InterruptedException e) { + LOG.log(Level.SEVERE, e.getMessage(), e); + + if (e instanceof InterruptedException) + Thread.currentThread() + .interrupt(); + } + + return isTokenValid; + } + +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java new file mode 100644 index 00000000..800433f6 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java @@ -0,0 +1,47 @@ +package com.oracle.database.jdbc.oauth; + +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The TokenGenerator class is a singleton utility for generating and verifying a unique authorization token. + * It uses a randomly generated UUID as the token, which is created once during class initialization. + * This class provides methods to retrieve the singleton instance and to verify if a given token matches the generated one. + *

+ * Note: This implementation generates the token only once per JVM session and logs it at the INFO level. + *

+ */ +public class TokenGenerator { + private static final Logger LOG = Logger.getLogger(TokenGenerator.class.getName()); + private static final TokenGenerator INSTANCE = new TokenGenerator(); + + private final String generatedToken; + + private TokenGenerator() { + generatedToken = UUID.randomUUID().toString(); + LOG.log(Level.INFO, "Authorization token generated: {0}", generatedToken); + } + + /** + * Returns the singleton instance of the TokenGenerator. + * This method ensures that only one instance of the class exists throughout the application. + * + * @return the singleton TokenGenerator instance + */ + public static TokenGenerator getInstance() { + return INSTANCE; + } + + /** + * Verifies if the provided token matches the internally generated token. + * This method performs a case-sensitive string comparison. + * + * @param token the token to verify against the generated token + * @return true if the provided token equals the generated token, false otherwise + */ + public boolean verifyToken(final String token) { + return generatedToken.equals(token); + } + +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java new file mode 100644 index 00000000..1b89e754 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java @@ -0,0 +1,62 @@ +package com.oracle.database.jdbc.web; + +import com.oracle.database.jdbc.oauth.OAuth2Configuration; +import com.oracle.database.jdbc.oauth.OAuth2TokenValidator; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +public class AuthorizationFilter implements Filter { + private static final OAuth2TokenValidator VALIDATOR = new OAuth2TokenValidator(); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + if (OAuth2Configuration.getInstance().isAuthenticationEnabled()) { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + final HttpServletResponse httpResponse = (HttpServletResponse) response; + + final String authHeader = httpRequest.getHeader("Authorization"); + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + errorHeader(httpResponse); + return; + } + + final String token = authHeader.substring("Bearer ".length()).trim(); + if (!VALIDATOR.isTokenValid(token)) { + errorHeader(httpResponse); + return; + } + } + + // token is valid + chain.doFilter(request, response); + } + + private void errorHeader(HttpServletResponse httpResponse) throws IOException { + final var serverURL = System.getProperty("serverURL", "http://localhost:45450"); + final var resourceMetadataURL = serverURL + "/.well-known/oauth-protected-resource"; + + httpResponse.setHeader("WWW-Authenticate", + "Bearer error=\"invalid_request\", " + + "error_description=\"No access token was provided in this request\", " + + "resource_metadata=\"" + resourceMetadataURL + "\""); + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + final String json = """ + { + "error": "invalid_request", + "error_description": "Access token is invalid or not present in this request", + "resource_metadata": "%s" + } + """.formatted(resourceMetadataURL); + httpResponse.getWriter() + .write(json); + } + +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java new file mode 100644 index 00000000..a219d2a1 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java @@ -0,0 +1,36 @@ +package com.oracle.database.jdbc.web; + +import com.oracle.database.jdbc.oauth.OAuth2Configuration; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +public class WellKnownServlet extends HttpServlet { + private static final String MCP_ENDPOINT = System.getProperty("serverURL", "http://localhost:45450") + "/mcp"; + private static final String ALLOWED_HOSTS = System.getProperty("allowedHosts","*"); + + private static final OAuth2Configuration OAUTH2_CONFIG = OAuth2Configuration.getInstance(); + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (!OAUTH2_CONFIG.isOAuth2Configured() || !OAUTH2_CONFIG.isAuthenticationEnabled()) { + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + return; + } + + response.setContentType("application/json"); + response.addHeader("Access-Control-Allow-Origin", ALLOWED_HOSTS); + response.setStatus(HttpServletResponse.SC_OK); + + final String json = """ + { + "resource":"%s", + "authorization_servers":["%s"] + }""".formatted(MCP_ENDPOINT, OAUTH2_CONFIG.getAuthServer()); + response.getWriter() + .write(json); + } +} From 1415e528c50332ffda919dfc75c04b7ce76b66a5 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 14:51:20 +0100 Subject: [PATCH 17/77] Fix OracleJDBCLogAnalyzer class name --- .../java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java index 71e87498..aa07cc60 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java @@ -32,7 +32,7 @@ * to analyze and process Oracle JDBC and RDBMS/SQLNet log files. *

*/ -public final class OracleJDBCLogAnalyzerMCPServer { +public final class OracleJDBCLogAnalyzer { private static final String FILE_PATH = "filePath"; private static final String SECOND_FILE_PATH = "secondFilePath"; From adf1d05072c84867eaac1e0601a552bf5161be8b Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 14:51:49 +0100 Subject: [PATCH 18/77] Use JUL for logging --- .../java/com/oracle/database/jdbc/Utils.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 4f50b17e..ba93ba8d 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -52,6 +52,7 @@ * {@link #useDataSource(DataSource)}. */ public class Utils { + private static final Logger LOG = Logger.getLogger(Utils.class.getName()); private static volatile DataSource dataSource; private static volatile Supplier connectionSupplier; @@ -78,7 +79,7 @@ static void useDataSource(DataSource ds) { *

*/ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) { - List specs = OracleJDBCLogAnalyzerMCPServer.getLogAnalyzerTools(); + List specs = OracleJDBCLogAnalyzer.getLogAnalyzerTools(); for (McpServerFeatures.SyncToolSpecification spec : specs) { String toolName = spec.tool().name(); // e.g. "get-stats", "get-queries" if (isToolEnabled(config, toolName)) { @@ -169,8 +170,7 @@ static ServerConfig loadConfig() { yamlConfig = yaml.loadAs(reader, ConfigRoot.class); } } catch (Exception e) { - Logger logger = Logger.getLogger(OracleDBToolboxMCPServer.class.getName()); - logger.log(Level.SEVERE, e.getMessage()); + LOG.log(Level.SEVERE, e.getMessage(), e); } if (yamlConfig == null) { config = ServerConfig.fromSystemProperties(); @@ -241,7 +241,7 @@ private static DataSource getDataSource(ServerConfig cfg) throws SQLException { /** *

- * Executes the provided {@link OracleJDBCLogAnalyzerMCPServer.ThrowingSupplier ThrowingSupplier} action, + * Executes the provided {@link OracleJDBCLogAnalyzer.ThrowingSupplier ThrowingSupplier} action, * which may throw an {@link Exception}, and returns the resulting {@link McpSchema.CallToolResult}. *
* If the action executes successfully, its {@link McpSchema.CallToolResult} is returned as-is. @@ -295,7 +295,7 @@ static void installExternalExtensionsFromDir() { final Path root = Paths.get(dir); if (!Files.isDirectory(root)) { - System.err.println("[mcp-ojdbc] ojdbc.ext.dir is not a directory: " + dir); + LOG.warning("[oracle-db-toolbox-mcp-server] ojdbc.ext.dir is not a directory: " + dir); return; } final List jarUrls = new ArrayList<>(); @@ -305,16 +305,16 @@ static void installExternalExtensionsFromDir() { try { jarUrls.add(p.toUri().toURL()); } catch (Exception e) { - System.err.println("[mcp-ojdbc] Failed to add jar: " + p + " -> " + e); + LOG.log(Level.WARNING, "[oracle-db-toolbox-mcp-server] Failed to add jar: " + p, e); } }); } catch (Exception e) { - System.err.println("[mcp-ojdbc] Failed to scan " + dir + " -> " + e); + LOG.log(Level.WARNING, "[oracle-db-toolbox-mcp-server] Failed to scan " + dir, e); return; } if (jarUrls.isEmpty()) { - System.err.println("[mcp-ojdbc] No jars found under " + dir); + LOG.warning("[oracle-db-toolbox-mcp-server] No jars found under " + dir); return; } From 9897a9b4fa0033e30a784b5f04bcf4c9dbc12972 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 16:23:22 +0100 Subject: [PATCH 19/77] hanlde NPE by logging info when YAML config file is not provided --- .../src/main/java/com/oracle/database/jdbc/Utils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index ba93ba8d..060518ca 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -169,6 +169,8 @@ static ServerConfig loadConfig() { Yaml yaml = new Yaml(); yamlConfig = yaml.loadAs(reader, ConfigRoot.class); } + } catch (NullPointerException ignored) { + LOG.info("YAML config file is not specified. Using values from system properties."); } catch (Exception e) { LOG.log(Level.SEVERE, e.getMessage(), e); } From ba3605c3b2a0fd3564352e6beb67031cc8ab18ea Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 16:31:16 +0100 Subject: [PATCH 20/77] Add HTTP Authentication Configuration section and system properties --- src/oracle-db-toolbox-mcp-server/README.md | 104 ++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 7613bfb5..a39af1e7 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -170,6 +170,71 @@ you can use the `mcp-remote` workaround: } ``` +### HTTP Authentication Configuration + +If you want to configure authentication for the HTTP server, you must provide the following system properties: + +- `-DenableAuthentication`: (default: `false`) If it's enabled (e.g. set to `true`) without properly configuring the OAuth2, +the MCP Server will generate a token (for development and testing purposes) once per JVM session and logs it at the INFO level, +which needs to be provided in the Authorization header of each request using `Bearer `. +- `-DauthServer`: The OAuth2 server URL which MUST provide the `/.well-known/oauth-authorization-server`. +- `-DintrospectionEndpoint`: The OAuth2 server's introspection endpoint used to validate an access token. +- `-DclientId`: Client ID (e.g. `oracle-db-toolbox`) +- `-DclientSecret`: Client Secret (e.g. `Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0`) +- `-DallowedHosts`: (default: `*`) The value of `Access-Control-Allow-Origin` header when requesting the `/.well-known/oauth-protected-resource` endpoint of the MCP Server. + +#### Examples + +##### Enabling Authentication with OAuth2 + +```bash +java \ + -Ddb.url=jdbc:oracle:thin:@host:1521/service \ + -Dtransport=http \ + -Dhttp.port=45450 \ + -DenableAuthentication=true \ + -DauthServer=http://localhost:8080/realms/mcp \ + -DintrospectionEndpoint=http://localhost:8080/realms/mcp/protocol/openid-connect/token/introspect \ + -DclientId=oracle-db-toolbox \ + -DclientSecret=Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0 \ + -DallowedHosts=http://localhost:6274 \ + -jar /oracle-db-toolbox-mcp-server-1.0.0.jar +``` + +In the above example, we configured OAuth2 with a local KeyCloak server with a realm named `mcp`, and we only allowed a local [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) +running at to retrieve the data from + + +##### Enabling Authentication without OAuth2 + +_Note: This mode is used only for local development and testing purposes._ + +```bash +java \ + -Ddb.url=jdbc:oracle:thin:@host:1521/service \ + -Dtransport=http \ + -Dhttp.port=45450 \ + -DenableAuthentication=true \ + -jar /oracle-db-toolbox-mcp-server-1.0.0.jar +``` +After starting the server, a UUID token will be generated and logged at INFO level: + +```log +Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.OAuth2Configuration +INFO: Authentication is enabled +Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.OAuth2Configuration +WARNING: OAuth2 is not configured +Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.TokenGenerator +INFO: Authorization token generated: 0dd11948-37a3-470f-911e-4cd8b3d6f69c +Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.OracleDBToolboxMCPServer startHttpServer +INFO: [oracle-db-toolbox-mcp-server] HTTP transport started on http://localhost:45450 (endpoint: http://localhost:45450/mcp) +``` + +And the token must be included in the http request header (e.g. `Authorization: Bearer 0dd11948-37a3-470f-911e-4cd8b3d6f69c`). + +For more details regarding this MCP and OAuth, please see [MCP specification for authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) (or a newer version if available). + + ## YAML Configuration Support The MCP server supports loading database connection and tool definitions from a YAML configuration file. @@ -264,9 +329,46 @@ java -DconfigFile=/path/to/config.yaml -jar .jar configFile No - Path to a YAML file defining `sources` and `tools`. + Path to a YAML file defining sources and tools. /opt/mcp/config.yaml + + enableAuthentication + No + Whether HTTP authentication is required or not (default false).
+ All the subsequent OAuth2 system properties are ignored if this property is set to false. + -DenableAuthentication=true + + + authServer + No + Configure the OAuth2 server URL + -DauthServer=http://localhost:8080/realms/master + + + introspectionEndpoint + No + The OAuth2 server endpoint used to validate and obtain metadata about an access token. + -DintrospectionEndpoint=http://localhost:8080/realms/mcp/protocol/openid-connect/token/introspect + + + clientId + No + The client identifier for registering with the configured OAuth2 server. + -DclientId=oracle-db-toolbox + + + clientSecret + No + The confidential key used to authenticate the client to the configured authorization server during the OAuth2 flow. + -DclientSecret=Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0 + + + allowedHosts + No + The Access-Control-Allow-Origin header value when making a request to the MCP Server's /.well-known/oauth-protected-resource endpoint (default * e.g. all hosts are allowed). + -DallowedHosts=http://localhost:6274 + From abdcdeea7e624697999fcd3d5e68ecb6f1760ae5 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 16:50:53 +0100 Subject: [PATCH 21/77] Remove LICENSE.txt file --- src/oracle-db-toolbox-mcp-server/LICENSE.txt | 35 -------------------- 1 file changed, 35 deletions(-) delete mode 100644 src/oracle-db-toolbox-mcp-server/LICENSE.txt diff --git a/src/oracle-db-toolbox-mcp-server/LICENSE.txt b/src/oracle-db-toolbox-mcp-server/LICENSE.txt deleted file mode 100644 index 8dc7c070..00000000 --- a/src/oracle-db-toolbox-mcp-server/LICENSE.txt +++ /dev/null @@ -1,35 +0,0 @@ -Copyright (c) 2025 Oracle and/or its affiliates. - -The Universal Permissive License (UPL), Version 1.0 - -Subject to the condition set forth below, permission is hereby granted to any -person obtaining a copy of this software, associated documentation and/or data -(collectively the "Software"), free of charge and under any and all copyright -rights in the Software, and any and all patent rights owned or freely -licensable by each licensor hereunder covering either (i) the unmodified -Software as contributed to or provided by such licensor, or (ii) the Larger -Works (as defined below), to deal in both - -(a) the Software, and -(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -one is included with the Software (each a "Larger Work" to which the Software -is contributed by such licensors), - -without restriction, including without limitation the rights to copy, create -derivative works of, display, perform, and distribute the Software and make, -use, sell, offer for sale, import, export, have made, and have sold the -Software and the Larger Work(s), and to sublicense the foregoing rights on -either these or other terms. - -This license is subject to the following condition: -The above copyright notice and either this complete permission notice or at -a minimum a reference to the UPL must be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file From e2bfe6de37eaf2bfa92359e6d85fdc6f66cc0f48 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 25 Nov 2025 16:51:04 +0100 Subject: [PATCH 22/77] Remove THIRD_PARTY_LICENSES.txt file --- .../THIRD_PARTY_LICENSES.txt | 887 ------------------ 1 file changed, 887 deletions(-) delete mode 100644 src/oracle-db-toolbox-mcp-server/THIRD_PARTY_LICENSES.txt diff --git a/src/oracle-db-toolbox-mcp-server/THIRD_PARTY_LICENSES.txt b/src/oracle-db-toolbox-mcp-server/THIRD_PARTY_LICENSES.txt deleted file mode 100644 index 698dc944..00000000 --- a/src/oracle-db-toolbox-mcp-server/THIRD_PARTY_LICENSES.txt +++ /dev/null @@ -1,887 +0,0 @@ ------------------------- Third-Party Components --------------------------------- -MCP Java SDK 0.10.0 - -*************************** -------------------------------- License and Notices for MCP Java SDK - - -Copyright: the original author or authors -=== Source URL: https://github.com/modelcontextprotocol/java-sdk/tree/v0.10.0/mcp -License: MIT - - ./LICENSE - -MIT License - -Copyright (c) 2025 the original author or authors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------- Separator -------------- - ------------------------------------ Fourth-party dependencies ------------------------------------ - -Dependency: ch.randelshofer:fastdoubleparser -Copyright: Werner Randelshofer -=== Source URL: https://github.com/wrandelshofer/FastDoubleParser/tree/v1.0.0/fastdoubleparser -License: MIT - === https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE - -Copyright 2022 Tim Buktu - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------- Separator -------------- - - === https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL - -Copyright (c) Daniel Lemire - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. --------------- Separator -------------- - - === https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT - -MIT License - -Copyright (c) 2021 The fast_float authors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - --------------- Separator -------------- - - ./LICENSE - -MIT License - -Copyright (c) 2023 Werner Randelshofer, Switzerland. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------- Separator -------------- - - === https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE - -MIT License - -Copyright (c) 2023 Werner Randelshofer, Switzerland. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------- Separator -------------- - - ./NOTICE - -# FastDoubleParser - -This is a Java port of Daniel Lemire's fast_float project. -This project provides parsers for double, float, BigDecimal and BigInteger values. - -## Copyright - -Copyright © 2023 Werner Randelshofer, Switzerland. - -## Licensing - -This code is licensed under MIT License. -https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE -(The file 'LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - -Some portions of the code have been derived from other projects. -All these projects require that we include a copyright notice, and some require that we also include some text of their -license file. - -fast_double_parser, Copyright (c) 2022 Daniel Lemire. BSL License. -https://github.com/lemire/fast_double_parser -https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL -(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - -fast_float, Copyright (c) 2021 The fast_float authors. MIT License. -https://github.com/fastfloat/fast_float -https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT -(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - -bigint, Copyright 2020 Tim Buktu. 2-clause BSD License. -https://github.com/tbuktu/bigint/tree/floatfft -https://github.com/tbuktu/bigint/blob/617c8cd8a7c5e4fb4d919c6a4d11e2586107f029/LICENSE -https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE -(We only use those portions of the bigint project that can be licensed under 2-clause BSD License.) -(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project -- as is required by that license.) - --------------- Separator -------------- -Dependency: com.fasterxml.jackson.core:jackson-annotations -Copyright: FasterXML,LLC -=== Source URL: https://github.com/FasterXML/jackson-annotations/tree/jackson-annotations-2.17.0 -License: Apache 2.0 - ./LICENSE - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --------------- Separator -------------- - - ./src/main/resources/META-INF/NOTICE - -# Jackson JSON processor - -Jackson is a high-performance, Free/Open Source JSON processing library. -It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has -been in development since 2007. -It is currently developed by a community of developers. - -## Copyright - -Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) - -## Licensing - -Jackson 2.x core and extension components are licensed under Apache License 2.0 -To find the details that apply to this artifact see the accompanying LICENSE file. - -## Credits - -A list of contributors may be found from CREDITS(-2.x) file, which is included -in some artifacts (usually source distributions); but is always available -from the source code management (SCM) system project uses. - --------------- Separator -------------- -Dependency: com.fasterxml.jackson.core:jackson-core -Copyright: FasterXML,LLC -=== Source URL: https://github.com/FasterXML/jackson-core/tree/jackson-core-2.17.0 -License: Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./LICENSE - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./src/main/resources/META-INF/jackson-core-NOTICE - -# Jackson JSON processor - -Jackson is a high-performance, Free/Open Source JSON processing library. -It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has -been in development since 2007. -It is currently developed by a community of developers. - -## Copyright - -Copyright 2007-, Tatu Saloranta (tatu.saloranta@iki.fi) - -## Licensing - -Jackson 2.x core and extension components are licensed under Apache License 2.0 -To find the details that apply to this artifact see the accompanying LICENSE file. - -## Credits - -A list of contributors may be found from CREDITS(-2.x) file, which is included -in some artifacts (usually source distributions); but is always available -from the source code management (SCM) system project uses. - -## FastDoubleParser - -jackson-core bundles a shaded copy of FastDoubleParser . -That code is available under an MIT license -under the following copyright. - -Copyright © 2023 Werner Randelshofer, Switzerland. MIT License. - -See FastDoubleParser-NOTICE for details of other source code included in FastDoubleParser -and the licenses and copyrights that apply to that code. - --------------- Separator -------------- -Dependency: com.fasterxml.jackson.core:jackson-databind -Copyright: FasterXML,LLC -=== Source URL: https://github.com/FasterXML/jackson-databind/tree/jackson-databind-2.17.0 -License: Apache 2.0 - ./LICENSE - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./src/main/resources/META-INF/NOTICE - -(Notice same as ./src/main/resources/META-INF/NOTICE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- -Dependency: io.projectreactor:reactor-core -Copyright: VMware Inc. -=== Source URL: https://github.com/reactor/reactor-core/tree/v3.7.0 -License: Apache 2.0 - ./codequality/spotless/licenseSlashstarStyle.txt - -/* - * Copyright (c) $YEAR VMware Inc. or its affiliates, All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - --------------- Separator -------------- - - ./LICENSE - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- -Dependency: net.bytebuddy:byte-buddy -Copyright: Rafael Winterhalter -=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy -License: Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - -Apache 2.0 - ( same as ./LICENSE of com.fasterxml.jackson.core:jackson-annotations) - --------------- Separator -------------- - - ./src/main/resources/META-INF/LICENSE - -This product bundles ASM ${version.asm}, which is available under a "3-clause BSD" -license. For details, see licenses/ASM. For more information visit ${asm.url}. - --------------- Separator -------------- -Dependency: net.bytebuddy:byte-buddy-dep -Copyright: Rafael Winterhalter -=== Source URL: https://github.com/raphw/byte-buddy/tree/byte-buddy-1.14.9/byte-buddy-dep -License: Apache 2.0 - ./LICENSE - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - --------------- Separator -------------- - - ./NOTICE - -Copyright ${project.inceptionYear} - Present ${copyright.holder} - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - --------------- Separator -------------- -Dependency: org.ow2.asm:asm -Copyright: INRIA, France Telecom -=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm -License: BSD 3-Clause - ./LICENSE.txt - -BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) - --------------- Separator -------------- -Dependency: org.ow2.asm:asm-commons -Copyright: INRIA, France Telecom -=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-commons -License: BSD 3-Clause - ./LICENSE.txt - - - ASM: a very small and fast Java bytecode manipulation framework - Copyright (c) 2000-2011 INRIA, France Telecom - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. - --------------- Separator -------------- -Dependency: org.ow2.asm:asm-tree -Copyright: INRIA, France Telecom -=== Source URL: https://gitlab.ow2.org/asm/asm/-/tree/ASM_9_6/asm-tree -License: BSD 3-Clause - ./LICENSE.txt - -BSD 3-Clause ( same as ./LICENSE.txt of org.ow2.asm:asm-commons) - --------------- Separator -------------- -Dependency: org.reactivestreams:reactive-streams -Copyright: Reactive Streams -=== Source URL: https://github.com/reactive-streams/reactive-streams-jvm/tree/v1.0.4 -License: MIT - ./LICENSE - -MIT No Attribution - -Copyright 2014 Reactive Streams - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --------------- Separator -------------- -Dependency: org.slf4j:slf4j-api -Copyright: QOS.ch -=== Source URL: https://github.com/qos-ch/slf4j/tree/v_2.0.16/slf4j-api -License: MIT - ./LICENSE.txt - -Copyright (c) 2004-2023 QOS.ch -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - - --------------- Separator -------------- - - - -=== Entry created by OSCS on 2025-07-03 09:39:14 - From fdaeec501d0594595b77f96985d865e59f9b674d Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Tue, 25 Nov 2025 17:26:15 +0100 Subject: [PATCH 23/77] Adding support for env variables within yaml file --- .../oracle/database/jdbc/EnvSubstitutor.java | 23 +++++++++++++++++++ .../java/com/oracle/database/jdbc/Utils.java | 1 + .../database/jdbc/config/ConfigRoot.java | 13 +++++++++++ .../database/jdbc/config/SourceConfig.java | 15 ++++++++++-- .../database/jdbc/config/ToolConfig.java | 14 +++++++++++ .../jdbc/config/ToolParameterConfig.java | 8 +++++++ 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/EnvSubstitutor.java diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/EnvSubstitutor.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/EnvSubstitutor.java new file mode 100644 index 00000000..786e5f64 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/EnvSubstitutor.java @@ -0,0 +1,23 @@ +package com.oracle.database.jdbc; + +import java.util.regex.*; + +public class EnvSubstitutor { + private static final Pattern PLACEHOLDER = Pattern.compile("\\$\\{([^}]+)}"); + + public static String substituteEnvVars(String input) { + if (input == null) return null; + Matcher m = PLACEHOLDER.matcher(input); + StringBuffer sb = new StringBuffer(); + while (m.find()) { + String var = m.group(1); + String value = System.getenv(var); + if (value == null) { + throw new IllegalStateException("Missing environment variable for: " + var); + } + m.appendReplacement(sb, Matcher.quoteReplacement(value)); + } + m.appendTail(sb); + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 060518ca..13d71ca8 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -184,6 +184,7 @@ static ServerConfig loadConfig() { entry.getValue().name = entry.getKey(); } } + yamlConfig.substituteEnvVars(); } return config; } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java index 215f7d12..b22bacd7 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java @@ -5,4 +5,17 @@ public class ConfigRoot { public Map sources; public Map tools; + + public void substituteEnvVars() { + if (sources != null) { + for (SourceConfig sc : sources.values()) { + if (sc != null) sc.substituteEnvVars(); + } + } + if (tools != null) { + for (ToolConfig tc : tools.values()) { + if (tc != null) tc.substituteEnvVars(); + } + } + } } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java index 31956f28..8bcf2632 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java @@ -1,8 +1,10 @@ package com.oracle.database.jdbc.config; +import com.oracle.database.jdbc.EnvSubstitutor; + public class SourceConfig { public String host; - public int port; + public String port; public String database; // Oracle SID or service name public String url; public String user; @@ -10,9 +12,18 @@ public class SourceConfig { public String toJdbcUrl() { if(url == null) { - return String.format("jdbc:oracle:thin:@%s:%d/%s", host, port, database); + return String.format("jdbc:oracle:thin:@%s:%s/%s", host, port, database); } else { return url; } } + + public void substituteEnvVars() { + this.host = EnvSubstitutor.substituteEnvVars(this.host); + this.port = EnvSubstitutor.substituteEnvVars(this.port); + this.database = EnvSubstitutor.substituteEnvVars(this.database); + this.url = EnvSubstitutor.substituteEnvVars(this.url); + this.user = EnvSubstitutor.substituteEnvVars(this.user); + this.password = EnvSubstitutor.substituteEnvVars(this.password); + } } \ No newline at end of file diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java index 8b723bfa..701e1774 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java @@ -1,5 +1,7 @@ package com.oracle.database.jdbc.config; +import com.oracle.database.jdbc.EnvSubstitutor; + import java.util.List; public class ToolConfig { @@ -8,4 +10,16 @@ public class ToolConfig { public String description; public List parameters; public String statement; // The SQL statement to execute + + public void substituteEnvVars() { + this.name = EnvSubstitutor.substituteEnvVars(this.name); + this.source = EnvSubstitutor.substituteEnvVars(this.source); + this.description = EnvSubstitutor.substituteEnvVars(this.description); + this.statement = EnvSubstitutor.substituteEnvVars(this.statement); + if (this.parameters != null) { + for (ToolParameterConfig param : this.parameters) { + if (param != null) param.substituteEnvVars(); + } + } + } } \ No newline at end of file diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java index 9145e8fb..4f7211cc 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java @@ -1,7 +1,15 @@ package com.oracle.database.jdbc.config; +import com.oracle.database.jdbc.EnvSubstitutor; + public class ToolParameterConfig { public String name; public String type; public String description; + + public void substituteEnvVars() { + this.name = EnvSubstitutor.substituteEnvVars(this.name); + this.type = EnvSubstitutor.substituteEnvVars(this.type); + this.description = EnvSubstitutor.substituteEnvVars(this.description); + } } \ No newline at end of file From 4c34fb2c04fba75d06e1e2e5b58a1a55fef94bab Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Tue, 25 Nov 2025 17:44:33 +0100 Subject: [PATCH 24/77] Add Dockerfile and container usage documentation --- src/oracle-db-toolbox-mcp-server/Dockerfile | 28 ++++++++ src/oracle-db-toolbox-mcp-server/README.md | 75 +++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/oracle-db-toolbox-mcp-server/Dockerfile diff --git a/src/oracle-db-toolbox-mcp-server/Dockerfile b/src/oracle-db-toolbox-mcp-server/Dockerfile new file mode 100644 index 00000000..e2c9524e --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/Dockerfile @@ -0,0 +1,28 @@ +# ---------- 1) Build stage ---------- +FROM eclipse-temurin:17-jdk-jammy AS builder + +RUN apt-get update -y \ + && apt-get install -y --no-install-recommends maven ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /src + +COPY . . + +RUN mvn -B -q -DskipTests clean package + +# ---------- 2) Runtime stage ---------- +FROM eclipse-temurin:17-jre-jammy + +RUN useradd -r -u 10001 appuser && \ + mkdir -p /app /ext && chown -R appuser:appuser /app /ext + +WORKDIR /app +USER appuser + +COPY --from=builder /src/target/oracle-db-toolbox-mcp-server-*.jar \ + /app/oracle-db-toolbox-mcp-server.jar + +EXPOSE 45450 + +ENTRYPOINT ["java", "-jar", "/app/oracle-db-toolbox-mcp-server.jar"] \ No newline at end of file diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index a39af1e7..7d013bc8 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -380,3 +380,78 @@ If you enable **only** the Log Analyzer tools, you can omit db.url. * Note: If you’re using token-based authentication (e.g., IAM tokens) or a centralized configuration provided via the JARs you place in `-Dojdbc.ext.dir`, you can omit `db.user` and `db.password`. The driver will pick up credentials and security settings from those extensions. + +## Docker Image + +A `Dockerfile` is included at the root of the project so you can build and run the MCP server as a container. + +### Build the image +From the project root (where the Dockerfile lives): + +```bash +podman build -t oracle-db-toolbox-mcp-server:1.0.0 . +``` +### Run the container (HTTP mode example) +This example runs the MCP server over HTTP inside the container and exposes it on port 45450 on your host. + +```json +podman run --rm \ + -p 45450:45450 \ + -e JAVA_TOOL_OPTIONS="\ + -Dtransport=http \ + -Dhttp.port=45450 \ + -Dtools=get-stats,get-queries \ + -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ + -Ddb.user=your_user \ + -Ddb.password=your_password" \ + oracle-db-toolbox-mcp-server:1.0.0 +``` +This exposes the MCP endpoint at: http://localhost:45450/mcp + +You can then configure Cline or Claude Desktop as described in the Using HTTP from Cline / Claude Desktop sections above. + +If you need extra JDBC / security jars (e.g. `oraclepki`, `wallets`, `centralized config`), +mount them and point `ojdbc.ext.dir` at that directory: + +```json +podman run --rm \ + -p 45450:45450 \ + -v /path/to/ext:/ext:ro \ + -e JAVA_TOOL_OPTIONS="\ + -Dtransport=http \ + -Dhttp.port=45450 \ + -Dtools=get-stats,get-queries \ + -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ + -Ddb.user=your_user \ + -Ddb.password=your_password" \ + -Dojdbc.ext.dir=/ext" \ + oracle-db-toolbox-mcp-server:1.0.0 +``` + +### Using Docker/Podman with stdio +Instead of running the MCP server over HTTP, you can keep using the **stdio** transport +and let your MCP client spawn the container (via **podman run**) instead of spawning java directly. +In this mode, the MCP client talks to the server over stdin/stdout, just like with a local JAR. + +#### Example: Claude Desktop using Podman (stdio) +In this configuration, Claude Desktop runs `podman run --rm -i ... and connects to the server via stdio: + +```json +{ + "mcpServers": { + "oracle-db-toolbox-mcp-server": { + "command": "podman", + "args": [ + "run", + "--rm", + "-i", + "-v", "/absolute/path/to/ext:/ext:ro", + "-e", + "JAVA_TOOL_OPTIONS=-Dtools=get-stats,get-queries -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service -Ddb.user=your_user -Ddb.password=your_password -Dojdbc.ext.dir=/ext -DconfigFile=/config/config.yaml", + "oracle-db-toolbox-mcp-server:1.0.0" + ] + } + } +} + +``` From 3501993748d56ec4f464015da04ff25de65200ec Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Tue, 25 Nov 2025 18:11:46 +0100 Subject: [PATCH 25/77] Update json with bash tags in readme --- src/oracle-db-toolbox-mcp-server/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 7d013bc8..d6e5b69e 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -394,7 +394,7 @@ podman build -t oracle-db-toolbox-mcp-server:1.0.0 . ### Run the container (HTTP mode example) This example runs the MCP server over HTTP inside the container and exposes it on port 45450 on your host. -```json +```bash podman run --rm \ -p 45450:45450 \ -e JAVA_TOOL_OPTIONS="\ @@ -413,7 +413,7 @@ You can then configure Cline or Claude Desktop as described in the Using HTTP fr If you need extra JDBC / security jars (e.g. `oraclepki`, `wallets`, `centralized config`), mount them and point `ojdbc.ext.dir` at that directory: -```json +```bash podman run --rm \ -p 45450:45450 \ -v /path/to/ext:/ext:ro \ @@ -453,5 +453,4 @@ In this configuration, Claude Desktop runs `podman run --rm -i ... and connects } } } - ``` From 28a696750e81007ec87c058e69494a8b381195d7 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Tue, 25 Nov 2025 18:16:09 +0100 Subject: [PATCH 26/77] Updating list of tools --- .../main/java/com/oracle/database/jdbc/ServerConfig.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java index 72fc2cd5..0c6f015e 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java @@ -53,10 +53,7 @@ private ServerConfig( } private static final Set DB_TOOLS = Set.of( - "execute_query","read_query","write_query","create_table","delete_table", - "list_tables","describe_table","start_transaction","resume_transaction", - "commit_transaction","rollback_transaction","exec_in_tx","db_ping", - "db_metrics_range","similarity_search" + "similarity_search", "explain_plan" ); @@ -144,7 +141,7 @@ static ServerConfig fromSystemProperties() { * “enable every tool” and returns {@code null}. * * Examples: - * "read_query,write_query" -> ["read_query","write_query"] + * "similarity_search,explain_plan" -> ["similarity_search","explain_plan"] * "*" or "all" or "" -> null (treat as all tools enabled) * * @param prop comma-separated tool names From 8087b9f3f0dc1c8704c30c5ccc0f04b0d8a7208b Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Tue, 25 Nov 2025 18:28:25 +0100 Subject: [PATCH 27/77] updating readme --- src/oracle-db-toolbox-mcp-server/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index a39af1e7..f82cf84f 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -237,7 +237,8 @@ For more details regarding this MCP and OAuth, please see [MCP specification for ## YAML Configuration Support -The MCP server supports loading database connection and tool definitions from a YAML configuration file. +The MCP server supports loading database connection and tool definitions from a YAML configuration file. +This file can contain environment variables as well. **Example `config.yaml`:** ```yaml From 7c5fa2692fc807fa8fe0c4e0ffc810fda2fb5bea Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Tue, 25 Nov 2025 18:35:46 +0100 Subject: [PATCH 28/77] Remove EXPOSE 45450 from Dockerfile --- src/oracle-db-toolbox-mcp-server/Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/Dockerfile b/src/oracle-db-toolbox-mcp-server/Dockerfile index e2c9524e..e8b4d8fc 100644 --- a/src/oracle-db-toolbox-mcp-server/Dockerfile +++ b/src/oracle-db-toolbox-mcp-server/Dockerfile @@ -23,6 +23,4 @@ USER appuser COPY --from=builder /src/target/oracle-db-toolbox-mcp-server-*.jar \ /app/oracle-db-toolbox-mcp-server.jar -EXPOSE 45450 - ENTRYPOINT ["java", "-jar", "/app/oracle-db-toolbox-mcp-server.jar"] \ No newline at end of file From 6f25fad1cb4e0c0652f9b977fbba156c34ab91fd Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 26 Nov 2025 08:47:41 +0100 Subject: [PATCH 29/77] Add WebUtils class with buildURLFromRequest static method --- .../oracle/database/jdbc/web/WebUtils.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java new file mode 100644 index 00000000..a0de468a --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java @@ -0,0 +1,52 @@ +package com.oracle.database.jdbc.web; + +import jakarta.servlet.http.HttpServletRequest; + +import java.util.Objects; + +/** + * Utility class for web-related operations, such as URL construction from HTTP requests. + * This class is not intended to be instantiated and provides only static methods. + */ +public class WebUtils { + + private WebUtils() {} + + /** + * Builds a URL string from the given {@link HttpServletRequest}, considering + * forwarded protocol headers and default port handling. + *

+ * The method first checks for the {@code X-Forwarded-Proto} header to determine + * the schema ({@code http} or {@code https}). + * If the header is missing, empty, or invalid, it falls back to the request's scheme. + * It then appends the server name and, if necessary, the port number + * (omitting defaults: 80 for http, 443 for https). + *

+ * + * @param request the {@link HttpServletRequest} from which to build the URL; must not be null + * @return a string representing the constructed URL in the format {@code scheme://serverName[:port]} + * @throws NullPointerException if the request parameter is null + */ + static String buildURLFromRequest(final HttpServletRequest request) { + Objects.requireNonNull(request, "request cannot be null"); + + final StringBuilder url = new StringBuilder(); + + String schema = request.getHeader("X-Forwarded-Proto"); + if (schema == null || schema.isEmpty() || + !("http".equalsIgnoreCase(schema) || "https".equalsIgnoreCase(schema))) + schema = request.getScheme(); + + url.append(schema) + .append("://") + .append(request.getServerName()); + + final int port = request.getServerPort(); + + if (!("http".equals(request.getScheme()) && port == 80) && + !("https".equals(request.getScheme()) && port == 443)) + url.append(":").append(port); + + return url.toString(); + } +} From caae5f7064f42554f713da10c33dba4c769e7352 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 26 Nov 2025 08:49:12 +0100 Subject: [PATCH 30/77] Remove buildServerURL method --- .../jdbc/OracleDBToolboxMCPServer.java | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 519103d0..076cf3ad 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -13,7 +13,6 @@ import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.*; -import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import javax.sql.DataSource; @@ -120,10 +119,7 @@ private static McpSyncServer startHttpServer() { jetty.start(); - final String url = buildServerURL(jetty); - final String mcpEndpoint = url + "/mcp"; - - LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on %s (endpoint: %s)".formatted(url, mcpEndpoint)); + LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + port + " (endpoint: /mcp)"); return server; } catch (Exception e) { @@ -131,31 +127,4 @@ private static McpSyncServer startHttpServer() { } } - private static String buildServerURL(Server jetty) { - String host = "localhost"; - String protocol = "http"; - int port = 45450; - - for (final org.eclipse.jetty.server.Connector conn : jetty.getConnectors()) - if (conn instanceof ServerConnector serverConnector) { - if (serverConnector.getHost() != null) { - host = serverConnector.getHost(); - port = serverConnector.getPort(); - } - - for (final org.eclipse.jetty.server.ConnectionFactory factory : serverConnector.getConnectionFactories()) - if (factory instanceof SslContextFactory || factory.getProtocol().toLowerCase().contains("ssl")) { - protocol = "https"; - } - - break; - } - - final var url = "%s://%s:%s".formatted(protocol, host, port); - - System.setProperty("serverURL", url); - - return url; - } - } From 41ac36544d053f3324cf5ea41662a9784b014542 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 26 Nov 2025 08:50:26 +0100 Subject: [PATCH 31/77] Rename errorHeader method to handleError and add HttpServletRequest param to use WebUtils.buildURLFromRequest --- .../database/jdbc/web/AuthorizationFilter.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java index 1b89e754..e23c510b 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java @@ -24,13 +24,13 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha final String authHeader = httpRequest.getHeader("Authorization"); if (authHeader == null || !authHeader.startsWith("Bearer ")) { - errorHeader(httpResponse); + handleError(httpResponse, httpRequest); return; } final String token = authHeader.substring("Bearer ".length()).trim(); if (!VALIDATOR.isTokenValid(token)) { - errorHeader(httpResponse); + handleError(httpResponse, httpRequest); return; } } @@ -39,19 +39,19 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha chain.doFilter(request, response); } - private void errorHeader(HttpServletResponse httpResponse) throws IOException { - final var serverURL = System.getProperty("serverURL", "http://localhost:45450"); + private void handleError(HttpServletResponse httpResponse, HttpServletRequest httpRequest) throws IOException { + final String serverURL = WebUtils.buildURLFromRequest(httpRequest); final var resourceMetadataURL = serverURL + "/.well-known/oauth-protected-resource"; + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.setHeader("WWW-Authenticate", "Bearer error=\"invalid_request\", " + - "error_description=\"No access token was provided in this request\", " + + "error_description=\"Access token is invalid or not provided in the request\", " + "resource_metadata=\"" + resourceMetadataURL + "\""); - httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); final String json = """ { "error": "invalid_request", - "error_description": "Access token is invalid or not present in this request", + "error_description": "Access token is invalid or not provided in the request", "resource_metadata": "%s" } """.formatted(resourceMetadataURL); From e7a26c180a695cb56e1963985629986a26d8d918 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 26 Nov 2025 08:51:41 +0100 Subject: [PATCH 32/77] Use WebUtils.buildURLFromRequest to buil the MCP endpoint --- .../java/com/oracle/database/jdbc/web/WellKnownServlet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java index a219d2a1..b68b5c9f 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java @@ -9,7 +9,6 @@ import java.io.IOException; public class WellKnownServlet extends HttpServlet { - private static final String MCP_ENDPOINT = System.getProperty("serverURL", "http://localhost:45450") + "/mcp"; private static final String ALLOWED_HOSTS = System.getProperty("allowedHosts","*"); private static final OAuth2Configuration OAUTH2_CONFIG = OAuth2Configuration.getInstance(); @@ -24,12 +23,13 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t response.setContentType("application/json"); response.addHeader("Access-Control-Allow-Origin", ALLOWED_HOSTS); response.setStatus(HttpServletResponse.SC_OK); + final String mcpEndpoint = WebUtils.buildURLFromRequest(request) + "/mcp"; final String json = """ { "resource":"%s", "authorization_servers":["%s"] - }""".formatted(MCP_ENDPOINT, OAUTH2_CONFIG.getAuthServer()); + }""".formatted(mcpEndpoint, OAUTH2_CONFIG.getAuthServer()); response.getWriter() .write(json); } From 860019a86fc79409c225f91a3cb2f61849afd783 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 26 Nov 2025 12:29:09 +0100 Subject: [PATCH 33/77] Add 'redirectOAuthToOpenID' system property to support OAuth servers that only provide '/.well-known/openid-configuration' endpoint --- src/oracle-db-toolbox-mcp-server/README.md | 14 ++++++++--- .../jdbc/OracleDBToolboxMCPServer.java | 6 +++++ .../web/RedirectOAuthToOpenIDServlet.java | 21 ++++++++++++++++ .../oracle/database/jdbc/web/WebUtils.java | 25 ++++++++++++++++++- .../database/jdbc/web/WellKnownServlet.java | 11 ++++---- 5 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/RedirectOAuthToOpenIDServlet.java diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 1794560f..d133ba2f 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -177,11 +177,13 @@ If you want to configure authentication for the HTTP server, you must provide th - `-DenableAuthentication`: (default: `false`) If it's enabled (e.g. set to `true`) without properly configuring the OAuth2, the MCP Server will generate a token (for development and testing purposes) once per JVM session and logs it at the INFO level, which needs to be provided in the Authorization header of each request using `Bearer `. -- `-DauthServer`: The OAuth2 server URL which MUST provide the `/.well-known/oauth-authorization-server`. -- `-DintrospectionEndpoint`: The OAuth2 server's introspection endpoint used to validate an access token. +- `-DauthServer`: The OAuth2 server URL which MUST provide the `/.well-known/oauth-authorization-server`. But if the authorization server only provides the `/.well-known/openid-configuration` you can enable `-DredirectOAuthToOpenID`. +- `-DredirectOAuthToOpenID`: (default: `false`) This system property is used to as a workaround to support OAuth servers that provide `/.well-known/openid-configuration` and not `/.well-known/oauth-authorization-server`. +It works by creating an `/.well-known/oauth-authorization-server` endpoint on the MCP Server that redirects to the OAuth server's `/.well-known/openid-configuration` endpoint. +- `-DintrospectionEndpoint`: The OAuth2 server's introspection endpoint used to validate an access token (The OAuth2 introspection JSON response MUST contain the `active` field, e.g. `{...,"active": false,..}`). - `-DclientId`: Client ID (e.g. `oracle-db-toolbox`) - `-DclientSecret`: Client Secret (e.g. `Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0`) -- `-DallowedHosts`: (default: `*`) The value of `Access-Control-Allow-Origin` header when requesting the `/.well-known/oauth-protected-resource` endpoint of the MCP Server. +- `-DallowedHosts`: (default: `*`) The value of `Access-Control-Allow-Origin` header when requesting the `/.well-known/oauth-protected-resource` endpoint (and `/.well-known/oauth-authorization-server` if `-DredirectOAuthToOpenID` is set to `true`) of the MCP Server. #### Examples @@ -370,6 +372,12 @@ java -DconfigFile=/path/to/config.yaml -jar .jar The Access-Control-Allow-Origin header value when making a request to the MCP Server's /.well-known/oauth-protected-resource endpoint (default * e.g. all hosts are allowed). -DallowedHosts=http://localhost:6274 + + redirectOAuthToOpenID + No + System property that redirects MCP Server's /.well-known/oauth-authorization-server endpoint to the OAuth server's /.well-known/openid-configuration as a workaround for servers lacking the former (default value is false. If OAuth is not properly configured, then this system property is ignored). + -DredirectOAuthToOpenID=false + diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 076cf3ad..1ce5c260 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -1,7 +1,10 @@ package com.oracle.database.jdbc; import com.fasterxml.jackson.databind.ObjectMapper; +import com.oracle.database.jdbc.oauth.OAuth2Configuration; import com.oracle.database.jdbc.web.AuthorizationFilter; +import com.oracle.database.jdbc.web.RedirectOAuthToOpenIDServlet; +import com.oracle.database.jdbc.web.WebUtils; import com.oracle.database.jdbc.web.WellKnownServlet; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpSyncServer; @@ -112,6 +115,9 @@ private static McpSyncServer startHttpServer() { context.addServlet(new ServletHolder(transport), "/mcp/*"); context.addServlet(WellKnownServlet.class.getName(), "/.well-known/oauth-protected-resource"); + if (OAuth2Configuration.getInstance().isOAuth2Configured() && WebUtils.isRedirectOpenIDToOAuthEnabled()) + context.addServlet(RedirectOAuthToOpenIDServlet.class.getName(), "/.well-known/oauth-authorization-server"); + var oauthFilter = new FilterHolder(new AuthorizationFilter()); context.addFilter(oauthFilter, "/mcp/*", null); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/RedirectOAuthToOpenIDServlet.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/RedirectOAuthToOpenIDServlet.java new file mode 100644 index 00000000..70db4859 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/RedirectOAuthToOpenIDServlet.java @@ -0,0 +1,21 @@ +package com.oracle.database.jdbc.web; + +import com.oracle.database.jdbc.oauth.OAuth2Configuration; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +public class RedirectOAuthToOpenIDServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + final String redirectLink = OAuth2Configuration.getInstance().getAuthServer() + + "/.well-known/openid-configuration"; + + response.addHeader("Access-Control-Allow-Origin", WebUtils.getAllowedHosts()); + response.sendRedirect(redirectLink); + } +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java index a0de468a..683a3091 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java @@ -5,7 +5,7 @@ import java.util.Objects; /** - * Utility class for web-related operations, such as URL construction from HTTP requests. + * Utility class for web-related operations, such as reading web-related system properties. * This class is not intended to be instantiated and provides only static methods. */ public class WebUtils { @@ -49,4 +49,27 @@ static String buildURLFromRequest(final HttpServletRequest request) { return url.toString(); } + + /** + * Retrieves the value of the system property {@code allowedHosts}. + * If the property is not set, it defaults to {@code "*"}. + * + * @return the value of the {@code allowedHosts} system property, or {@code "*"} if not set + */ + static String getAllowedHosts() { + return System.getProperty("allowedHosts","*"); + } + + /** + * Checks if redirection from OpenID to OAuth is enabled by examining the + * system property {@code redirectOpenIDToOAuth}. If the property is set to + * "true" (case-insensitive), the method returns {@code true}; otherwise, + * it returns {@code false}. If the property is not set, it defaults to {@code false}. + * + * @return {@code true} if redirection from OpenID to OAuth is enabled, {@code false} otherwise + */ + public static boolean isRedirectOpenIDToOAuthEnabled() { + return Boolean.parseBoolean(System.getProperty("redirectOpenIDToOAuth", "false")); + } + } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java index b68b5c9f..6103f308 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java @@ -9,8 +9,6 @@ import java.io.IOException; public class WellKnownServlet extends HttpServlet { - private static final String ALLOWED_HOSTS = System.getProperty("allowedHosts","*"); - private static final OAuth2Configuration OAUTH2_CONFIG = OAuth2Configuration.getInstance(); @Override @@ -21,15 +19,18 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } response.setContentType("application/json"); - response.addHeader("Access-Control-Allow-Origin", ALLOWED_HOSTS); + response.addHeader("Access-Control-Allow-Origin", WebUtils.getAllowedHosts()); response.setStatus(HttpServletResponse.SC_OK); - final String mcpEndpoint = WebUtils.buildURLFromRequest(request) + "/mcp"; + + final String serverURL = WebUtils.buildURLFromRequest(request); + final String mcpEndpoint = serverURL + "/mcp"; + String authServer = WebUtils.isRedirectOpenIDToOAuthEnabled() ? serverURL : OAUTH2_CONFIG.getAuthServer(); final String json = """ { "resource":"%s", "authorization_servers":["%s"] - }""".formatted(mcpEndpoint, OAUTH2_CONFIG.getAuthServer()); + }""".formatted(mcpEndpoint, authServer); response.getWriter() .write(json); } From 105e88239e4dba4dd223edb0df9e9a44051049bd Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Wed, 26 Nov 2025 22:17:44 +0100 Subject: [PATCH 34/77] Switch from jetty http server to tomcat server --- src/oracle-db-toolbox-mcp-server/pom.xml | 17 ++---- .../jdbc/OracleDBToolboxMCPServer.java | 54 +++++++++++-------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/pom.xml b/src/oracle-db-toolbox-mcp-server/pom.xml index f4af5027..766d42c1 100644 --- a/src/oracle-db-toolbox-mcp-server/pom.xml +++ b/src/oracle-db-toolbox-mcp-server/pom.xml @@ -33,7 +33,7 @@ 23.9.0.25.07 2.5 1.0.0 - 12.1.1 + 11.0.14 5.10.0 ${java.version} ${java.version} @@ -72,18 +72,11 @@ ${logAnalyzer.version}
- + - org.eclipse.jetty - jetty-server - ${jetty.version} - - - - - org.eclipse.jetty.ee10 - jetty-ee10-servlet - ${jetty.version} + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 1ce5c260..8bf7567a 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -12,14 +12,14 @@ import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import io.modelcontextprotocol.spec.McpSchema; -import org.eclipse.jetty.ee10.servlet.FilterHolder; -import org.eclipse.jetty.ee10.servlet.ServletContextHandler; -import org.eclipse.jetty.ee10.servlet.ServletHolder; -import org.eclipse.jetty.server.*; -import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; import javax.sql.DataSource; +import java.io.File; import java.util.logging.Logger; import static com.oracle.database.jdbc.Utils.installExternalExtensionsFromDir; @@ -80,7 +80,7 @@ private OracleDBToolboxMCPServer() { } /** - * Start HTTP Streamable MCP transport on /mcp using Jetty. + * Start HTTP Streamable MCP transport on /mcp using Tomcat. */ private static McpSyncServer startHttpServer() { try { @@ -102,28 +102,38 @@ private static McpSyncServer startHttpServer() { .immediateExecution(true) .build(); - var threadPool = new QueuedThreadPool(); - threadPool.setName("oracle-db-toolbox-mcp-server"); - var jetty = new Server(threadPool); + Tomcat tomcat = new Tomcat(); + tomcat.setPort(port); + tomcat.getConnector(); - var connector = new ServerConnector(jetty); - connector.setPort(port); - jetty.addConnector(connector); + String ctxPath = ""; + String docBase = new File(".").getAbsolutePath(); + Context ctx = tomcat.addContext(ctxPath, docBase); - var context = new ServletContextHandler(); - context.setContextPath("/"); - context.addServlet(new ServletHolder(transport), "/mcp/*"); - context.addServlet(WellKnownServlet.class.getName(), "/.well-known/oauth-protected-resource"); + Tomcat.addServlet(ctx, "mcpServlet", transport); + ctx.addServletMappingDecoded("/mcp/*", "mcpServlet"); - if (OAuth2Configuration.getInstance().isOAuth2Configured() && WebUtils.isRedirectOpenIDToOAuthEnabled()) - context.addServlet(RedirectOAuthToOpenIDServlet.class.getName(), "/.well-known/oauth-authorization-server"); + Tomcat.addServlet(ctx, "wellKnownServlet", new WellKnownServlet()); + ctx.addServletMappingDecoded( + "/.well-known/oauth-protected-resource", "wellKnownServlet"); - var oauthFilter = new FilterHolder(new AuthorizationFilter()); - context.addFilter(oauthFilter, "/mcp/*", null); + if (OAuth2Configuration.getInstance().isOAuth2Configured() && WebUtils.isRedirectOpenIDToOAuthEnabled()) { + Tomcat.addServlet(ctx, "redirectOAuthToOpenIDServlet", new RedirectOAuthToOpenIDServlet()); + ctx.addServletMappingDecoded("/.well-known/oauth-authorization-server", "redirectOAuthToOpenIDServlet"); + } + + FilterDef filterDef = new FilterDef(); + filterDef.setFilterName("authFilter"); + filterDef.setFilter(new AuthorizationFilter()); + filterDef.setFilterClass(AuthorizationFilter.class.getName()); + ctx.addFilterDef(filterDef); - jetty.setHandler(context); + FilterMap filterMap = new FilterMap(); + filterMap.setFilterName("authFilter"); + filterMap.addURLPattern("/mcp/*"); + ctx.addFilterMap(filterMap); - jetty.start(); + tomcat.start(); LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + port + " (endpoint: /mcp)"); From b83d84a2887fcdd1570cde20c663de7b1c83df5c Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 27 Nov 2025 16:27:01 +0100 Subject: [PATCH 35/77] Adding support for http + ssl --- src/oracle-db-toolbox-mcp-server/README.md | 11 +++- .../jdbc/OracleDBToolboxMCPServer.java | 54 +++++++++++++++++-- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index d133ba2f..25fd2fb1 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -136,6 +136,9 @@ java \ ``` This exposes the MCP endpoint at: `http://localhost:45450/mcp`. +#### Note +You can enable http + ssl by adding the path to your certificate and its password through -DcertificatePath and -DcertificatePassword. You can also change the port from -DhttpsPort (45451 by default). + ### Using HTTP from Cline Cline supports streamable HTTP directly. Example: @@ -406,16 +409,19 @@ This example runs the MCP server over HTTP inside the container and exposes it o ```bash podman run --rm \ -p 45450:45450 \ + -p 45451:45451 \ + -v /path/to/certificate:/app/certif.p12:ro,z \ -e JAVA_TOOL_OPTIONS="\ -Dtransport=http \ -Dhttp.port=45450 \ + -Dhttps.port=45451 \ -Dtools=get-stats,get-queries \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ -Ddb.password=your_password" \ oracle-db-toolbox-mcp-server:1.0.0 ``` -This exposes the MCP endpoint at: http://localhost:45450/mcp +This exposes the MCP endpoint at: http://[your-ip-address]:45450/mcp or https://[your-ip-address]:45451/mcp You can then configure Cline or Claude Desktop as described in the Using HTTP from Cline / Claude Desktop sections above. @@ -425,10 +431,13 @@ mount them and point `ojdbc.ext.dir` at that directory: ```bash podman run --rm \ -p 45450:45450 \ + -p 45451:45451 \ -v /path/to/ext:/ext:ro \ + -v /path/to/certificate:/app/certif.p12:ro,z \ -e JAVA_TOOL_OPTIONS="\ -Dtransport=http \ -Dhttp.port=45450 \ + -Dhttps.port=45451 \ -Dtools=get-stats,get-queries \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 8bf7567a..cd19f04c 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -16,6 +16,9 @@ import org.apache.catalina.startup.Tomcat; import org.apache.tomcat.util.descriptor.web.FilterDef; import org.apache.tomcat.util.descriptor.web.FilterMap; +import org.apache.catalina.connector.Connector; +import org.apache.tomcat.util.net.SSLHostConfig; +import org.apache.tomcat.util.net.SSLHostConfigCertificate; import javax.sql.DataSource; @@ -84,7 +87,7 @@ private OracleDBToolboxMCPServer() { */ private static McpSyncServer startHttpServer() { try { - int port = Integer.parseInt(System.getProperty("http.port", "45450")); + int httpPort = Integer.parseInt(System.getProperty("http.port", "45450")); HttpServletStreamableServerTransportProvider transport = HttpServletStreamableServerTransportProvider.builder() @@ -103,7 +106,7 @@ private static McpSyncServer startHttpServer() { .build(); Tomcat tomcat = new Tomcat(); - tomcat.setPort(port); + tomcat.setPort(httpPort); tomcat.getConnector(); String ctxPath = ""; @@ -133,9 +136,10 @@ private static McpSyncServer startHttpServer() { filterMap.addURLPattern("/mcp/*"); ctx.addFilterMap(filterMap); + enableHttps(tomcat); tomcat.start(); - LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + port + " (endpoint: /mcp)"); + LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + httpPort + " (endpoint: /mcp)"); return server; } catch (Exception e) { @@ -143,4 +147,48 @@ private static McpSyncServer startHttpServer() { } } + private static void enableHttps(Tomcat tomcat) { + try { + String keystorePath = System.getProperty("certificatePath"); + String keystorePassword = System.getProperty("certificatePassword"); + int httpsPort = Integer.parseInt(System.getProperty("https.port", "45451")); + + + // Create HTTPS connector + Connector https = new Connector("org.apache.coyote.http11.Http11NioProtocol"); + https.setPort(httpsPort); + https.setSecure(true); + https.setScheme("https"); + https.setProperty("SSLEnabled", "true"); + + // Create SSL config + SSLHostConfig sslHostConfig = new SSLHostConfig(); + sslHostConfig.setHostName("_default_"); + sslHostConfig.setProtocols("+TLSv1.2,+TLSv1.3"); + + + SSLHostConfigCertificate cert = new SSLHostConfigCertificate( + sslHostConfig, + SSLHostConfigCertificate.Type.RSA + ); + + cert.setCertificateKeystoreFile(keystorePath); + cert.setCertificateKeystorePassword(keystorePassword); + cert.setCertificateKeystoreType("PKCS12"); + + // Attach certificate to SSL config + sslHostConfig.addCertificate(cert); + + // Enable SSL + https.addSslHostConfig(sslHostConfig); + + // Register connector + tomcat.getService().addConnector(https); + + } catch (Exception e) { + throw new RuntimeException("Failed to enable HTTPS on Tomcat", e); + } + } + + } From 321892ba88a4ba6d215c851f80b6cc91eb1b77e9 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 27 Nov 2025 16:33:15 +0100 Subject: [PATCH 36/77] Updating readme --- src/oracle-db-toolbox-mcp-server/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 25fd2fb1..83515d1b 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -136,9 +136,14 @@ java \ ``` This exposes the MCP endpoint at: `http://localhost:45450/mcp`. -#### Note -You can enable http + ssl by adding the path to your certificate and its password through -DcertificatePath and -DcertificatePassword. You can also change the port from -DhttpsPort (45451 by default). - +### Note +You can enable HTTPS (SSL/TLS) by specifying the path to your certificate keystore and its password using the -DcertificatePath and -DcertificatePassword options. +Only PKCS12 (.p12 or .pfx) keystore format is supported. +You can also change the HTTPS port with the -DhttpsPort option (default is 45451). +##### Example +```shell +-DcertificatePath=/path/to/your-certificate.p12 -DcertificatePassword=yourPassword -DhttpsPort=443 +``` ### Using HTTP from Cline Cline supports streamable HTTP directly. Example: From 7efc45ca5fde4272abafd51edcc3fc036892f629 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Fri, 28 Nov 2025 12:12:53 +0100 Subject: [PATCH 37/77] Handling missing certificate info --- .../database/jdbc/OracleDBToolboxMCPServer.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index cd19f04c..8ce4e73e 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -136,7 +136,11 @@ private static McpSyncServer startHttpServer() { filterMap.addURLPattern("/mcp/*"); ctx.addFilterMap(filterMap); - enableHttps(tomcat); + String keystorePath = System.getProperty("certificatePath"); + String keystorePassword = System.getProperty("certificatePassword"); + if(keystorePath!=null && keystorePassword != null) enableHttps(tomcat, keystorePath, keystorePassword); + else LOG.warning("SSL setup is skipped: Keystore path or password not specified"); + tomcat.start(); LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + httpPort + " (endpoint: /mcp)"); @@ -147,13 +151,9 @@ private static McpSyncServer startHttpServer() { } } - private static void enableHttps(Tomcat tomcat) { + private static void enableHttps(Tomcat tomcat, String keystorePath, String keystorePassword) { try { - String keystorePath = System.getProperty("certificatePath"); - String keystorePassword = System.getProperty("certificatePassword"); int httpsPort = Integer.parseInt(System.getProperty("https.port", "45451")); - - // Create HTTPS connector Connector https = new Connector("org.apache.coyote.http11.Http11NioProtocol"); https.setPort(httpsPort); From 34bc91c25d0821b535a87a4a8568a46fbd9064b7 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Fri, 28 Nov 2025 12:33:25 +0100 Subject: [PATCH 38/77] updating readme --- src/oracle-db-toolbox-mcp-server/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 83515d1b..c02ccaf2 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -420,6 +420,8 @@ podman run --rm \ -Dtransport=http \ -Dhttp.port=45450 \ -Dhttps.port=45451 \ + -DcertificatePath=[path/to/certificate] \ + -DcertificatePassword=[password] \ -Dtools=get-stats,get-queries \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ From 14555fbc376b410b4b6bd36b5c6a558836963898 Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Fri, 28 Nov 2025 14:08:35 +0100 Subject: [PATCH 39/77] Switch Docker image to Oracle OpenJDK 17 --- src/oracle-db-toolbox-mcp-server/Dockerfile | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/Dockerfile b/src/oracle-db-toolbox-mcp-server/Dockerfile index e8b4d8fc..5e0285fc 100644 --- a/src/oracle-db-toolbox-mcp-server/Dockerfile +++ b/src/oracle-db-toolbox-mcp-server/Dockerfile @@ -1,9 +1,18 @@ # ---------- 1) Build stage ---------- -FROM eclipse-temurin:17-jdk-jammy AS builder +FROM container-registry.oracle.com/java/openjdk:17 AS builder -RUN apt-get update -y \ - && apt-get install -y --no-install-recommends maven ca-certificates \ - && rm -rf /var/lib/apt/lists/* +ARG MAVEN_VERSION=3.9.11 +ARG MAVEN_BASE_URL=https://dlcdn.apache.org/maven/maven-3 + +RUN curl -fsSL \ + ${MAVEN_BASE_URL}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + -o /tmp/maven.tar.gz \ + && tar -xzf /tmp/maven.tar.gz -C /opt \ + && ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven \ + && rm /tmp/maven.tar.gz + +ENV MAVEN_HOME=/opt/maven +ENV PATH="${MAVEN_HOME}/bin:${PATH}" WORKDIR /src @@ -12,7 +21,7 @@ COPY . . RUN mvn -B -q -DskipTests clean package # ---------- 2) Runtime stage ---------- -FROM eclipse-temurin:17-jre-jammy +FROM container-registry.oracle.com/java/openjdk:17 RUN useradd -r -u 10001 appuser && \ mkdir -p /app /ext && chown -R appuser:appuser /app /ext From 9a5a80ef1d1f177946d48429f4458f13307d056d Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Tue, 2 Dec 2025 20:04:14 +0100 Subject: [PATCH 40/77] Grouping all needed system properties into one class --- .../oracle/database/jdbc/LoadedConstants.java | 36 +++++++++++++++++++ .../jdbc/OracleDBToolboxMCPServer.java | 25 +++++-------- .../oracle/database/jdbc/ServerConfig.java | 16 ++++----- .../java/com/oracle/database/jdbc/Utils.java | 4 +-- .../jdbc/oauth/OAuth2Configuration.java | 12 ++++--- .../oracle/database/jdbc/web/WebUtils.java | 5 +-- 6 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java new file mode 100644 index 00000000..add4f575 --- /dev/null +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java @@ -0,0 +1,36 @@ +package com.oracle.database.jdbc; + +public final class LoadedConstants { + private LoadedConstants() {} // Prevent instantiation + + /** Network config */ + public static final String TRANSPORT_KIND = System.getProperty("transport", "stdio") + .trim() + .toLowerCase();; + public static final int HTTP_PORT = Integer.parseInt(System.getProperty("http.port", "45450")); + public static final int HTTPS_PORT = Integer.parseInt(System.getProperty("https.port", "45451")); + public static final String KEYSTORE_PATH = System.getProperty("certificatePath"); + public static final String KEYSTORE_PASSWORD = System.getProperty("certificatePassword"); + + /** Tools config */ + public static final String TOOLS = System.getProperty("tools"); + public static final String DB_URL = System.getProperty("db.url"); + public static final String DB_USER = System.getProperty("db.user"); + public static final String DB_PASSWORD = System.getProperty("db.password"); + + /** OAuth config */ + public static final String ALLOWED_HOSTS= System.getProperty("allowedHosts","*"); + public static final String REDIRECT_OPENID_TO_OAUTH= System.getProperty("redirectOpenIDToOAuth","false"); + public static final boolean ENABLE_AUTH = Boolean.parseBoolean(System.getProperty("enableAuthentication","false")); + public static final String AUTH_SERVER = System.getProperty("authServer"); + public static final String INTROSPECTION_ENDPOINT = System.getProperty("introspectionEndpoint"); + public static final String CLIENT_ID = System.getProperty("clientId"); + public static final String CLIENT_SECRET = System.getProperty("clientSecret"); + + /** Yaml config */ + public static final String CONFIG_FILE = System.getProperty("configFile"); + + /** External extensions */ + public static final String OJDBC_EXT_DIR = System.getProperty("ojdbc.ext.dir"); + +} diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 8ce4e73e..9e193ada 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -51,13 +51,9 @@ public static void useDataSource(DataSource ds) { public static void main(String[] args) { installExternalExtensionsFromDir(); - final String transportKind = System.getProperty("transport", "stdio") - .trim() - .toLowerCase(); - McpSyncServer server; - switch (transportKind) { + switch (LoadedConstants.TRANSPORT_KIND) { case "http" -> { server = startHttpServer(); } @@ -73,7 +69,7 @@ public static void main(String[] args) { .build(); } default -> throw new IllegalArgumentException( - "Unsupported transport: " + transportKind + " (expected 'stdio' or 'http')"); + "Unsupported transport: " + LoadedConstants.TRANSPORT_KIND + " (expected 'stdio' or 'http')"); } Utils.addSyncToolSpecifications(server, config); } @@ -87,8 +83,6 @@ private OracleDBToolboxMCPServer() { */ private static McpSyncServer startHttpServer() { try { - int httpPort = Integer.parseInt(System.getProperty("http.port", "45450")); - HttpServletStreamableServerTransportProvider transport = HttpServletStreamableServerTransportProvider.builder() .objectMapper(new ObjectMapper()) @@ -106,7 +100,7 @@ private static McpSyncServer startHttpServer() { .build(); Tomcat tomcat = new Tomcat(); - tomcat.setPort(httpPort); + tomcat.setPort(LoadedConstants.HTTP_PORT); tomcat.getConnector(); String ctxPath = ""; @@ -136,14 +130,14 @@ private static McpSyncServer startHttpServer() { filterMap.addURLPattern("/mcp/*"); ctx.addFilterMap(filterMap); - String keystorePath = System.getProperty("certificatePath"); - String keystorePassword = System.getProperty("certificatePassword"); - if(keystorePath!=null && keystorePassword != null) enableHttps(tomcat, keystorePath, keystorePassword); + if(LoadedConstants.KEYSTORE_PATH!=null && LoadedConstants.KEYSTORE_PASSWORD != null) { + enableHttps(tomcat, LoadedConstants.KEYSTORE_PATH, LoadedConstants.KEYSTORE_PASSWORD); + } else LOG.warning("SSL setup is skipped: Keystore path or password not specified"); tomcat.start(); - LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + httpPort + " (endpoint: /mcp)"); + LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + LoadedConstants.HTTP_PORT + " (endpoint: /mcp)"); return server; } catch (Exception e) { @@ -153,10 +147,9 @@ private static McpSyncServer startHttpServer() { private static void enableHttps(Tomcat tomcat, String keystorePath, String keystorePassword) { try { - int httpsPort = Integer.parseInt(System.getProperty("https.port", "45451")); // Create HTTPS connector Connector https = new Connector("org.apache.coyote.http11.Http11NioProtocol"); - https.setPort(httpsPort); + https.setPort(LoadedConstants.HTTPS_PORT); https.setSecure(true); https.setScheme("https"); https.setProperty("SSLEnabled", "true"); @@ -164,8 +157,6 @@ private static void enableHttps(Tomcat tomcat, String keystorePath, String keyst // Create SSL config SSLHostConfig sslHostConfig = new SSLHostConfig(); sslHostConfig.setHostName("_default_"); - sslHostConfig.setProtocols("+TLSv1.2,+TLSv1.3"); - SSLHostConfigCertificate cert = new SSLHostConfigCertificate( sslHostConfig, diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java index 0c6f015e..0df77d1c 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java @@ -75,12 +75,12 @@ private ServerConfig( * @throws IllegalStateException if required properties are missing from both system properties and YAML config */ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, String defaultSourceKey) { - Set tools = parseToolsProp(System.getProperty("tools")); + Set tools = parseToolsProp(LoadedConstants.TOOLS); boolean needDb = wantsAnyDbTools(tools); - String dbUrl = System.getProperty("db.url"); - String dbUser = System.getProperty("db.user"); - String dbPass = System.getProperty("db.password"); + String dbUrl = LoadedConstants.DB_URL; + String dbUser = LoadedConstants.DB_USER; + String dbPass = LoadedConstants.DB_PASSWORD; Map sources = configRoot != null ? configRoot.sources : Collections.emptyMap(); Map toolsMap = configRoot != null ? configRoot.tools : Collections.emptyMap(); @@ -116,18 +116,18 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St * @throws IllegalStateException if {@code db.url} is missing or blank */ static ServerConfig fromSystemProperties() { - Set tools = parseToolsProp(System.getProperty("tools")); + Set tools = parseToolsProp(LoadedConstants.TOOLS); boolean needDb = wantsAnyDbTools(tools); - String dbUrl = System.getProperty("db.url"); + String dbUrl = LoadedConstants.DB_URL; if (needDb && (dbUrl == null || dbUrl.isBlank())) { throw new IllegalStateException("Missing required system property: db.url"); } return new ServerConfig( dbUrl, - System.getProperty("db.user"), - System.getProperty("db.password"), + LoadedConstants.DB_USER, + LoadedConstants.DB_PASSWORD, tools, new HashMap<>(), new HashMap<>() diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 13d71ca8..415f54b3 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -162,7 +162,7 @@ static String getOrDefault(Object v, String def) { */ static ServerConfig loadConfig() { ServerConfig config; - String configFilePath = System.getProperty("configFile"); + String configFilePath = LoadedConstants.CONFIG_FILE; ConfigRoot yamlConfig = null; try { try (Reader reader = Files.newBufferedReader(Paths.get(configFilePath))) { @@ -291,7 +291,7 @@ public interface ThrowingSupplier { * */ static void installExternalExtensionsFromDir() { - final String dir = System.getProperty("ojdbc.ext.dir"); + final String dir = LoadedConstants.OJDBC_EXT_DIR; if (dir == null || dir.isBlank()) { return; } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java index 772147f9..9d5c3ecd 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java @@ -1,5 +1,7 @@ package com.oracle.database.jdbc.oauth; +import com.oracle.database.jdbc.LoadedConstants; + import java.util.logging.Logger; /** @@ -33,11 +35,11 @@ public class OAuth2Configuration { * If OAuth2 is not configured, initializes a TokenGenerator for local token generation. */ private OAuth2Configuration() { - isAuthenticationEnabled = Boolean.parseBoolean(System.getProperty("enableAuthentication","false")); - authServer = System.getProperty("authServer"); - introspectionEndpoint = System.getProperty("introspectionEndpoint"); - clientId = System.getProperty("clientId"); - clientSecret = System.getProperty("clientSecret"); + isAuthenticationEnabled = LoadedConstants.ENABLE_AUTH; + authServer = LoadedConstants.AUTH_SERVER; + introspectionEndpoint = LoadedConstants.INTROSPECTION_ENDPOINT; + clientId = LoadedConstants.CLIENT_ID; + clientSecret = LoadedConstants.CLIENT_SECRET; isOAuth2Configured = authServer != null && introspectionEndpoint != null && clientId != null && clientSecret != null; if (!isAuthenticationEnabled) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java index 683a3091..0802955c 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java @@ -1,5 +1,6 @@ package com.oracle.database.jdbc.web; +import com.oracle.database.jdbc.LoadedConstants; import jakarta.servlet.http.HttpServletRequest; import java.util.Objects; @@ -57,7 +58,7 @@ static String buildURLFromRequest(final HttpServletRequest request) { * @return the value of the {@code allowedHosts} system property, or {@code "*"} if not set */ static String getAllowedHosts() { - return System.getProperty("allowedHosts","*"); + return LoadedConstants.ALLOWED_HOSTS; } /** @@ -69,7 +70,7 @@ static String getAllowedHosts() { * @return {@code true} if redirection from OpenID to OAuth is enabled, {@code false} otherwise */ public static boolean isRedirectOpenIDToOAuthEnabled() { - return Boolean.parseBoolean(System.getProperty("redirectOpenIDToOAuth", "false")); + return Boolean.parseBoolean(LoadedConstants.REDIRECT_OPENID_TO_OAUTH); } } From ccf7bd71d5e3f07f22e3565054e74928af6b8eec Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Tue, 2 Dec 2025 20:58:36 +0100 Subject: [PATCH 41/77] Adding custom tool schemas for the custom tools --- .../java/com/oracle/database/jdbc/Utils.java | 2 +- .../database/jdbc/config/ToolConfig.java | 26 +++++++++++++++++++ .../jdbc/config/ToolParameterConfig.java | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 415f54b3..f40ad802 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -106,7 +106,7 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) .name(tc.name) .title(tc.name) .description(tc.description) - .inputSchema(ToolSchemas.SQL_ONLY) + .inputSchema(tc.buildInputSchemaJson()) .build() ) .callHandler((exchange, callReq) -> diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java index 701e1774..61076a0c 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java @@ -1,5 +1,8 @@ package com.oracle.database.jdbc.config; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.oracle.database.jdbc.EnvSubstitutor; import java.util.List; @@ -22,4 +25,27 @@ public void substituteEnvVars() { } } } + + public String buildInputSchemaJson() { + ObjectNode schema = JsonNodeFactory.instance.objectNode(); + schema.put("type", "object"); + ObjectNode properties = schema.putObject("properties"); + ArrayNode required = JsonNodeFactory.instance.arrayNode(); + + for (ToolParameterConfig param : this.parameters) { + if (param == null) { + continue; + } + ObjectNode prop = properties.putObject(param.name); + prop.put("type", param.type); + prop.put("description", param.description); + if (param.required) { + required.add(param.name); + } + } + if (!required.isEmpty()) { + schema.set("required", required); + } + return schema.toString(); + } } \ No newline at end of file diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java index 4f7211cc..b993a325 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java @@ -6,6 +6,7 @@ public class ToolParameterConfig { public String name; public String type; public String description; + public boolean required; public void substituteEnvVars() { this.name = EnvSubstitutor.substituteEnvVars(this.name); From efb731491deb50d0828ac2788e61414de89f3b47 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Tue, 2 Dec 2025 21:09:12 +0100 Subject: [PATCH 42/77] making yaml file work fine without sources --- .../src/main/java/com/oracle/database/jdbc/Utils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index f40ad802..07b67392 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -112,7 +112,7 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) .callHandler((exchange, callReq) -> tryCall(() -> { // Resolve source - SourceConfig src = config.sources.get(tc.source); + SourceConfig src = config.sources!=null?config.sources.get(tc.source):null; String jdbcUrl = (src != null) ? src.toJdbcUrl() : config.dbUrl; String dbUser = (src != null) ? src.user : config.dbUser; String dbPassword = (src != null) ? src.password : config.dbPassword; @@ -177,7 +177,7 @@ static ServerConfig loadConfig() { if (yamlConfig == null) { config = ServerConfig.fromSystemProperties(); } else { - String defaultSourceKey = yamlConfig.sources.keySet().stream().findFirst().orElseThrow(); + String defaultSourceKey = yamlConfig.sources!=null?yamlConfig.sources.keySet().stream().findFirst().orElse(null):null; config = ServerConfig.fromSystemPropertiesAndYaml(yamlConfig, defaultSourceKey); if (config.tools != null) { for (Map.Entry entry : config.tools.entrySet()) { From f1efc1333ff307499b537423b92226423d2b6791 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Wed, 3 Dec 2025 11:12:05 +0100 Subject: [PATCH 43/77] Updating multiple source config yaml behaviour and the default source behaviour. --- src/oracle-db-toolbox-mcp-server/README.md | 4 +- .../jdbc/ExplainAndExecutePlanTool.java | 2 +- .../jdbc/OracleDBToolboxMCPServer.java | 12 -- .../oracle/database/jdbc/ServerConfig.java | 19 +-- .../database/jdbc/SimilaritySearchTool.java | 2 +- .../java/com/oracle/database/jdbc/Utils.java | 110 +++++++----------- .../jdbc/oauth/OAuth2Configuration.java | 16 +++ 7 files changed, 77 insertions(+), 88 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index c02ccaf2..3f7f8979 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -247,7 +247,8 @@ For more details regarding this MCP and OAuth, please see [MCP specification for ## YAML Configuration Support -The MCP server supports loading database connection and tool definitions from a YAML configuration file. +The MCP server supports loading database connection and tool definitions from a YAML configuration file. +For sources, if a tool has a specific source it will use it. Otherwise, it will look for the default source which is either the source we got from system properties, otherwise, the first source defined in the file (if any). This file can contain environment variables as well. **Example `config.yaml`:** @@ -265,6 +266,7 @@ tools: - name: name type: string description: Hotel name to search for. + required: false statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' ``` To enable YAML configuration, launch the server with: diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java index 253e393b..7a217407 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java @@ -45,7 +45,7 @@ static McpServerFeatures.SyncToolSpecification getExplainAndExecutePlanTool(Serv .inputSchema(ToolSchemas.EXPLAIN_PLAN) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { - try (Connection c = openConnection(config)) { + try (Connection c = openConnection(config, null)) { final String sql = String.valueOf(callReq.arguments().get("sql")); if (sql == null || sql.isBlank()) { return new McpSchema.CallToolResult("Parameter 'sql' is required", true); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 9e193ada..70d4d353 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -36,18 +36,6 @@ public class OracleDBToolboxMCPServer { config = Utils.loadConfig(); } - /** - * Injects a custom {@link javax.sql.DataSource} used by all tools - * to obtain connections. - *

Call this before {@link #main(String[])} to override the default - * configuration-based data source.

- * - * @param ds the data source to use for all DB operations - */ - public static void useDataSource(DataSource ds) { - Utils.useDataSource(ds); - } - public static void main(String[] args) { installExternalExtensionsFromDir(); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java index 0df77d1c..787cc823 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java @@ -35,6 +35,7 @@ final class ServerConfig { public final Set toolsFilter; public final Map sources; public final Map tools; + public static String defaultSourceName; // Only if the default db info are from yaml config to avoid redundancy private ServerConfig( String dbUrl, @@ -85,14 +86,17 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St Map sources = configRoot != null ? configRoot.sources : Collections.emptyMap(); Map toolsMap = configRoot != null ? configRoot.tools : Collections.emptyMap(); - if ((dbUrl == null || dbUrl.isBlank() || - dbUser == null || dbUser.isBlank() || - dbPass == null || dbPass.isBlank()) - && sources.containsKey(defaultSourceKey)) { + boolean allLoadedConstantsPresent = + dbUrl != null && !dbUrl.isBlank() + && dbUser != null && !dbUser.isBlank() + && dbPass != null && !dbPass.isBlank(); + + if (!allLoadedConstantsPresent && sources!=null && sources.containsKey(defaultSourceKey)) { SourceConfig src = sources.get(defaultSourceKey); - if (dbUrl == null || dbUrl.isBlank()) dbUrl = src.toJdbcUrl(); - if (dbUser == null || dbUser.isBlank()) dbUser = src.user; - if (dbPass == null || dbPass.isBlank()) dbPass = src.password; + dbUrl = src.toJdbcUrl(); + dbUser = src.user; + dbPass = src.password; + defaultSourceName = defaultSourceKey; } if (needDb && (dbUrl == null || dbUrl.isBlank())) { @@ -104,7 +108,6 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St if (needDb && (dbPass == null || dbPass.isBlank())) { throw new IllegalStateException("Missing required db.password in both system properties and YAML config"); } - return new ServerConfig(dbUrl, dbUser, dbPass, tools, sources, toolsMap); } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java index 3dd36f49..4f139533 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java @@ -44,7 +44,7 @@ static McpServerFeatures.SyncToolSpecification getSymilaritySearchTool(ServerCon .inputSchema(ToolSchemas.SIMILARITY_SEARCH) .build()) .callHandler((exchange, callReq) -> tryCall(() -> { - try (Connection c = openConnection(config)) { + try (Connection c = openConnection(config, null)) { Map arguments = callReq.arguments(); String question = String.valueOf(arguments.get("question")); if (question == null || question.isBlank()) { diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 07b67392..022387d8 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -24,7 +24,6 @@ import java.security.Security; import java.sql.Clob; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -34,7 +33,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.function.Supplier; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; @@ -48,30 +47,12 @@ * database identifiers. * *

The connection pool uses minimal settings (1 connection). - * Applications can override the default data source via - * {@link #useDataSource(DataSource)}. */ public class Utils { private static final Logger LOG = Logger.getLogger(Utils.class.getName()); - private static volatile DataSource dataSource; - private static volatile Supplier connectionSupplier; - - /** - * Overrides the default data source with a caller-provided one. - * Call before the server starts registering tools. - * - * @param ds custom data source used to obtain connections - */ - static void useDataSource(DataSource ds) { - connectionSupplier = () -> { - try { - return ds.getConnection(); - } catch (SQLException e) { - throw new RuntimeException(e); - } - }; - } + private static final Map dataSources = new ConcurrentHashMap<>(); + private static volatile DataSource defaultDataSource; /** *

@@ -111,12 +92,7 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) ) .callHandler((exchange, callReq) -> tryCall(() -> { - // Resolve source - SourceConfig src = config.sources!=null?config.sources.get(tc.source):null; - String jdbcUrl = (src != null) ? src.toJdbcUrl() : config.dbUrl; - String dbUser = (src != null) ? src.user : config.dbUser; - String dbPassword = (src != null) ? src.password : config.dbPassword; - try (Connection c = DriverManager.getConnection(jdbcUrl, dbUser, dbPassword)) { + try (Connection c = openConnection(config, tc.source)) { PreparedStatement ps = c.prepareStatement(tc.statement); int paramIdx = 1; if (tc.parameters != null) { @@ -193,55 +169,59 @@ static ServerConfig loadConfig() { * Acquires a JDBC connection from the active data source. * * @param cfg server configuration + * @param sourceName database source * @return open JDBC connection * @throws SQLException on acquisition failure */ - static Connection openConnection(ServerConfig cfg) throws SQLException { - Supplier s = connectionSupplier; - if (s != null) { - try { - return s.get(); - } catch (RuntimeException re) { - if (re.getCause() instanceof SQLException se) throw se; - throw re; - } - } - return getDataSource(cfg).getConnection(); + static Connection openConnection(ServerConfig cfg, String sourceName) throws SQLException { + return getOrCreateDataSource(cfg, sourceName).getConnection(); } /** - * Lazily initializes and returns a UCP {@link PoolDataSource} using - * values from {@link ServerConfig}. The pool is kept minimal and - * predictable (initial/min/max = 1). + * Lazily initializes and returns a UCP {@link PoolDataSource} for the given source, + * using values from {@link ServerConfig}. Each source gets its own minimal pool. * + * @param cfg the server configuration + * @param sourceName the name of the source; if null, uses the default source + * @return a {@link DataSource} for the specified source * @throws SQLException if creation or configuration fails */ - private static DataSource getDataSource(ServerConfig cfg) throws SQLException { - if (dataSource != null) return dataSource; - synchronized (Utils.class) { - if (dataSource != null) - return dataSource; - - PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); - pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); - pds.setURL(cfg.dbUrl); - if (cfg.dbUser != null) - pds.setUser(cfg.dbUser); - if (cfg.dbPassword != null) - pds.setPassword(cfg.dbPassword); - - pds.setInitialPoolSize(1); - pds.setMinPoolSize(1); - pds.setConnectionWaitTimeout(10); - pds.setConnectionProperty("remarksReporting", "true"); - pds.setConnectionProperty("oracle.jdbc.vectorDefaultGetObjectType", "double[]"); - pds.setConnectionProperty("oracle.jdbc.jsonDefaultGetObjectType", "java.lang.String"); - - dataSource = pds; - return dataSource; + private static DataSource getOrCreateDataSource(ServerConfig cfg, String sourceName) throws SQLException { + if (sourceName == null || sourceName.equals(ServerConfig.defaultSourceName)) { + if (defaultDataSource != null) return defaultDataSource; + synchronized (Utils.class) { + if (defaultDataSource != null) return defaultDataSource; + defaultDataSource = createDataSource(cfg.dbUrl, cfg.dbUser, cfg.dbPassword); + return defaultDataSource; + } + } else { + return dataSources.computeIfAbsent(sourceName, name -> { + try { + SourceConfig src = (cfg.sources != null) ? cfg.sources.get(name) : null; + if (src == null) throw new IllegalArgumentException("Unknown source: " + name); + return createDataSource(src.toJdbcUrl(), src.user, src.password); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + }); } } + private static DataSource createDataSource(String url, String user, String password) throws SQLException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(url); + if (user != null) pds.setUser(user); + if (password != null) pds.setPassword(password); + pds.setInitialPoolSize(1); + pds.setMinPoolSize(1); + pds.setConnectionWaitTimeout(10); + pds.setConnectionProperty("remarksReporting", "true"); + pds.setConnectionProperty("oracle.jdbc.vectorDefaultGetObjectType", "double[]"); + pds.setConnectionProperty("oracle.jdbc.jsonDefaultGetObjectType", "java.lang.String"); + return pds; + } + /** *

* Executes the provided {@link OracleJDBCLogAnalyzer.ThrowingSupplier ThrowingSupplier} action, diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java index 9d5c3ecd..34fc7b28 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java @@ -51,6 +51,22 @@ private OAuth2Configuration() { LOG.info("OAuth2 is configured"); else { LOG.warning("OAuth2 is not configured"); + if (authServer != null || introspectionEndpoint != null || clientId != null || clientSecret != null) { + final var stringBuilder = new StringBuilder(); + if (authServer == null) + stringBuilder.append("Authentication server URL (-DauthServer) is missing"); + + if (introspectionEndpoint == null) + stringBuilder.append("Introspection endpoint (-DintrospectionEndpoint) is missing"); + + if (clientId == null) + stringBuilder.append("Client ID (-DclientId) is missing"); + + if (clientSecret == null) + stringBuilder.append("Client secret (-DclientSecret) is missing"); + + LOG.warning(stringBuilder.toString()); + } // Generate a local UUID string token TokenGenerator.getInstance(); } From 877c7b3f19cfa67567cb41afac153e3f12991156 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Fri, 5 Dec 2025 14:18:43 +0100 Subject: [PATCH 44/77] Making sure env variable substitution happens before getting default connection info --- .../main/java/com/oracle/database/jdbc/ServerConfig.java | 6 ++++++ .../src/main/java/com/oracle/database/jdbc/Utils.java | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java index 787cc823..2b65aaa2 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java @@ -86,6 +86,12 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St Map sources = configRoot != null ? configRoot.sources : Collections.emptyMap(); Map toolsMap = configRoot != null ? configRoot.tools : Collections.emptyMap(); + if (toolsMap != null) { + for (Map.Entry entry : toolsMap.entrySet()) { + entry.getValue().name = entry.getKey(); + } + } + configRoot.substituteEnvVars(); boolean allLoadedConstantsPresent = dbUrl != null && !dbUrl.isBlank() && dbUser != null && !dbUser.isBlank() diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index 022387d8..a99be4a4 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -155,12 +155,6 @@ static ServerConfig loadConfig() { } else { String defaultSourceKey = yamlConfig.sources!=null?yamlConfig.sources.keySet().stream().findFirst().orElse(null):null; config = ServerConfig.fromSystemPropertiesAndYaml(yamlConfig, defaultSourceKey); - if (config.tools != null) { - for (Map.Entry entry : config.tools.entrySet()) { - entry.getValue().name = entry.getKey(); - } - } - yamlConfig.substituteEnvVars(); } return config; } From 2632d7447d6b18240ff8bc1a8a095f623f51bb61 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Sun, 7 Dec 2025 19:05:51 +0100 Subject: [PATCH 45/77] Add getMissingConfigurationWarningMessage() method --- .../jdbc/oauth/OAuth2Configuration.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java index 34fc7b28..53ccd8ac 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java @@ -2,6 +2,8 @@ import com.oracle.database.jdbc.LoadedConstants; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Logger; /** @@ -52,27 +54,35 @@ private OAuth2Configuration() { else { LOG.warning("OAuth2 is not configured"); if (authServer != null || introspectionEndpoint != null || clientId != null || clientSecret != null) { - final var stringBuilder = new StringBuilder(); - if (authServer == null) - stringBuilder.append("Authentication server URL (-DauthServer) is missing"); + final var warningMessage = getMissingConfigurationWarningMessage(); - if (introspectionEndpoint == null) - stringBuilder.append("Introspection endpoint (-DintrospectionEndpoint) is missing"); - - if (clientId == null) - stringBuilder.append("Client ID (-DclientId) is missing"); - - if (clientSecret == null) - stringBuilder.append("Client secret (-DclientSecret) is missing"); - - LOG.warning(stringBuilder.toString()); + LOG.warning(warningMessage); } - // Generate a local UUID string token + // Generate a local UUID string token or load it from ORACLE_DB_TOOLBOX_AUTH_TOKEN env var. TokenGenerator.getInstance(); } } } + private String getMissingConfigurationWarningMessage() { + final List warningMessages = new ArrayList<>(); + final var mainMessage = "The following OAuth system properties are not configured correctly: "; + + if (authServer == null) + warningMessages.add("Authentication server URL (-DauthServer)"); + + if (introspectionEndpoint == null) + warningMessages.add("Introspection endpoint (-DintrospectionEndpoint)"); + + if (clientId == null) + warningMessages.add("Client ID (-DclientId)"); + + if (clientSecret == null) + warningMessages.add("Client secret (-DclientSecret)"); + + return mainMessage + String.join(", ", warningMessages); + } + /** * Returns the singleton instance of OAuth2Configuration. * From 668d84d453eca44b17e393337aab1ff788906643 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Sun, 7 Dec 2025 19:06:49 +0100 Subject: [PATCH 46/77] Add support for reading ORACLE_DB_TOOLBOX_AUTH_TOKEN env var and use it as a token --- src/oracle-db-toolbox-mcp-server/README.md | 37 +++++++++++++++---- .../oracle/database/jdbc/LoadedConstants.java | 3 +- .../database/jdbc/oauth/TokenGenerator.java | 19 ++++++---- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 3f7f8979..83beacac 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -180,11 +180,18 @@ you can use the `mcp-remote` workaround: ### HTTP Authentication Configuration -If you want to configure authentication for the HTTP server, you must provide the following system properties: +#### Generated Token (For Development and Testing) + +To enable authentication for the HTTP server, you must set the `-DenableAuthentication` system property to `true` (default value is `false`). +If it's enabled (e.g. set to `true`) the MCP Server will check if there's an environment variable called `ORACLE_DB_TOOLBOX_AUTH_TOKEN` and its value will be used as a token. +If the environment variable is not found, then a random UUID token will be generated once per JVM session. The token would be logged at the `INFO` level. + +When connecting to the MCP server, the token needs to be provided in the Authorization header of each request using the `Bearer ` prefix. + +#### OAuth2 Configuration + +In order to configure an OAuth2 server, the `-DenableAuthentication` should be enabled alongside the following system properties: -- `-DenableAuthentication`: (default: `false`) If it's enabled (e.g. set to `true`) without properly configuring the OAuth2, -the MCP Server will generate a token (for development and testing purposes) once per JVM session and logs it at the INFO level, -which needs to be provided in the Authorization header of each request using `Bearer `. - `-DauthServer`: The OAuth2 server URL which MUST provide the `/.well-known/oauth-authorization-server`. But if the authorization server only provides the `/.well-known/openid-configuration` you can enable `-DredirectOAuthToOpenID`. - `-DredirectOAuthToOpenID`: (default: `false`) This system property is used to as a workaround to support OAuth servers that provide `/.well-known/openid-configuration` and not `/.well-known/oauth-authorization-server`. It works by creating an `/.well-known/oauth-authorization-server` endpoint on the MCP Server that redirects to the OAuth server's `/.well-known/openid-configuration` endpoint. @@ -193,6 +200,8 @@ It works by creating an `/.well-known/oauth-authorization-server` endpoint on th - `-DclientSecret`: Client Secret (e.g. `Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0`) - `-DallowedHosts`: (default: `*`) The value of `Access-Control-Allow-Origin` header when requesting the `/.well-known/oauth-protected-resource` endpoint (and `/.well-known/oauth-authorization-server` if `-DredirectOAuthToOpenID` is set to `true`) of the MCP Server. +For more details regarding this MCP and OAuth, please see [MCP specification for authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) (or a newer version if available). + #### Examples ##### Enabling Authentication with OAuth2 @@ -217,7 +226,7 @@ running at to retrieve the data from +INFO: Authentication is enabled +Nov 25, 2025 4:10:26 PM com.oracle.database.jdbc.oauth.OAuth2Configuration +WARNING: OAuth2 is not configured +Nov 25, 2025 4:10:26 PM com.oracle.database.jdbc.oauth.TokenGenerator +INFO: Authorization token generated (for testing and development use only): Secret_DeV_T0ken +``` +Ultimately, the token must be included in the http request header (e.g. `Authorization: Bearer 0dd11948-37a3-470f-911e-4cd8b3d6f69c` or `Authorization: Bearer Secret_DeV_T0ken`). ## YAML Configuration Support diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java index add4f575..fa9f1ee5 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java @@ -6,7 +6,7 @@ private LoadedConstants() {} // Prevent instantiation /** Network config */ public static final String TRANSPORT_KIND = System.getProperty("transport", "stdio") .trim() - .toLowerCase();; + .toLowerCase(); public static final int HTTP_PORT = Integer.parseInt(System.getProperty("http.port", "45450")); public static final int HTTPS_PORT = Integer.parseInt(System.getProperty("https.port", "45451")); public static final String KEYSTORE_PATH = System.getProperty("certificatePath"); @@ -22,6 +22,7 @@ private LoadedConstants() {} // Prevent instantiation public static final String ALLOWED_HOSTS= System.getProperty("allowedHosts","*"); public static final String REDIRECT_OPENID_TO_OAUTH= System.getProperty("redirectOpenIDToOAuth","false"); public static final boolean ENABLE_AUTH = Boolean.parseBoolean(System.getProperty("enableAuthentication","false")); + public static final String ORACLE_DB_TOOLBOX_AUTH_TOKEN = System.getenv("ORACLE_DB_TOOLBOX_AUTH_TOKEN"); public static final String AUTH_SERVER = System.getProperty("authServer"); public static final String INTROSPECTION_ENDPOINT = System.getProperty("introspectionEndpoint"); public static final String CLIENT_ID = System.getProperty("clientId"); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java index 800433f6..4425ad58 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java @@ -1,16 +1,17 @@ package com.oracle.database.jdbc.oauth; +import static com.oracle.database.jdbc.LoadedConstants.ORACLE_DB_TOOLBOX_AUTH_TOKEN; + import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; /** - * The TokenGenerator class is a singleton utility for generating and verifying a unique authorization token. - * It uses a randomly generated UUID as the token, which is created once during class initialization. - * This class provides methods to retrieve the singleton instance and to verify if a given token matches the generated one. + * The TokenGenerator class is responsible for generating and verifying authorization tokens. + * It follows the singleton pattern to ensure a single instance throughout the application. *

- * Note: This implementation generates the token only once per JVM session and logs it at the INFO level. - *

+ * The generated token can be overridden using the {@code ORACLE_DB_TOOLBOX_AUTH_TOKEN} environment variable. + * If not overridden, a random UUID is used as the token. */ public class TokenGenerator { private static final Logger LOG = Logger.getLogger(TokenGenerator.class.getName()); @@ -18,9 +19,13 @@ public class TokenGenerator { private final String generatedToken; + /** + * Private constructor to prevent instantiation from outside the class. + * Initializes the generated token based on the {@code ORACLE_DB_TOOLBOX_AUTH_TOKEN} environment variable or a random UUID. + */ private TokenGenerator() { - generatedToken = UUID.randomUUID().toString(); - LOG.log(Level.INFO, "Authorization token generated: {0}", generatedToken); + generatedToken = ORACLE_DB_TOOLBOX_AUTH_TOKEN != null ? ORACLE_DB_TOOLBOX_AUTH_TOKEN : UUID.randomUUID().toString() ; + LOG.log(Level.INFO, "Authorization token generated (for testing and development use only): {0}", generatedToken); } /** From f8718bce544e1de6c4c1034a35da79982471e4d8 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Sun, 7 Dec 2025 19:09:17 +0100 Subject: [PATCH 47/77] Fix log line in enabling Authentication without OAuth2 section in README --- src/oracle-db-toolbox-mcp-server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 83beacac..cc26b5d9 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -244,7 +244,7 @@ INFO: Authentication is enabled Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.OAuth2Configuration WARNING: OAuth2 is not configured Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.TokenGenerator -INFO: Authorization token generated: 0dd11948-37a3-470f-911e-4cd8b3d6f69c +INFO: Authorization token generated (for testing and development use only): 0dd11948-37a3-470f-911e-4cd8b3d6f69c Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.OracleDBToolboxMCPServer startHttpServer INFO: [oracle-db-toolbox-mcp-server] HTTP transport started on http://localhost:45450 (endpoint: http://localhost:45450/mcp) ``` From 8aec6220611581a93c5b0fa63c7d37fc5f7af04c Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Mon, 8 Dec 2025 10:00:19 +0100 Subject: [PATCH 48/77] Making Http optional --- .../oracle/database/jdbc/LoadedConstants.java | 4 ++-- .../jdbc/OracleDBToolboxMCPServer.java | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java index add4f575..2d6c2201 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java @@ -7,8 +7,8 @@ private LoadedConstants() {} // Prevent instantiation public static final String TRANSPORT_KIND = System.getProperty("transport", "stdio") .trim() .toLowerCase();; - public static final int HTTP_PORT = Integer.parseInt(System.getProperty("http.port", "45450")); - public static final int HTTPS_PORT = Integer.parseInt(System.getProperty("https.port", "45451")); + public static final String HTTP_PORT = System.getProperty("http.port"); + public static final String HTTPS_PORT = System.getProperty("https.port"); public static final String KEYSTORE_PATH = System.getProperty("certificatePath"); public static final String KEYSTORE_PASSWORD = System.getProperty("certificatePassword"); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java index 70d4d353..8cd32dc5 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java @@ -20,8 +20,6 @@ import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.SSLHostConfigCertificate; -import javax.sql.DataSource; - import java.io.File; import java.util.logging.Logger; @@ -88,8 +86,14 @@ private static McpSyncServer startHttpServer() { .build(); Tomcat tomcat = new Tomcat(); - tomcat.setPort(LoadedConstants.HTTP_PORT); - tomcat.getConnector(); + if(LoadedConstants.HTTP_PORT!=null){ + tomcat.setPort(Integer.parseInt(LoadedConstants.HTTP_PORT)); + tomcat.getConnector(); + } else { + tomcat.setPort(-1); + LOG.warning("Http setup is skipped: http port is not specified"); + } + String ctxPath = ""; String docBase = new File(".").getAbsolutePath(); @@ -118,10 +122,10 @@ private static McpSyncServer startHttpServer() { filterMap.addURLPattern("/mcp/*"); ctx.addFilterMap(filterMap); - if(LoadedConstants.KEYSTORE_PATH!=null && LoadedConstants.KEYSTORE_PASSWORD != null) { + if(LoadedConstants.HTTPS_PORT!=null && LoadedConstants.KEYSTORE_PATH!=null && LoadedConstants.KEYSTORE_PASSWORD != null) { enableHttps(tomcat, LoadedConstants.KEYSTORE_PATH, LoadedConstants.KEYSTORE_PASSWORD); } - else LOG.warning("SSL setup is skipped: Keystore path or password not specified"); + else LOG.warning("SSL setup is skipped: Https port or Keystore path or password not specified"); tomcat.start(); @@ -137,7 +141,7 @@ private static void enableHttps(Tomcat tomcat, String keystorePath, String keyst try { // Create HTTPS connector Connector https = new Connector("org.apache.coyote.http11.Http11NioProtocol"); - https.setPort(LoadedConstants.HTTPS_PORT); + https.setPort(Integer.parseInt(LoadedConstants.HTTPS_PORT)); https.setSecure(true); https.setScheme("https"); https.setProperty("SSLEnabled", "true"); From b382503bfbf0739f17ef41b562cbdce920fc5298 Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Wed, 10 Dec 2025 12:19:44 +0100 Subject: [PATCH 49/77] Fix quoting typo in Docker JAVA_TOOL_OPTIONS example --- src/oracle-db-toolbox-mcp-server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index cc26b5d9..63633afe 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -473,7 +473,7 @@ podman run --rm \ -Dtools=get-stats,get-queries \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ - -Ddb.password=your_password" \ + -Ddb.password=your_password \ -Dojdbc.ext.dir=/ext" \ oracle-db-toolbox-mcp-server:1.0.0 ``` From cb487e266a40fcd839f3f8a5b18bf049f23f5123 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 10 Dec 2025 13:05:22 +0100 Subject: [PATCH 50/77] Rename log analyzer tools to include 'jdbc' or 'rdbms' keywords --- src/oracle-db-toolbox-mcp-server/README.md | 24 +++++------ .../database/jdbc/OracleJDBCLogAnalyzer.java | 42 +++++++++---------- .../jdbc/OracleJDBCLogAnalyzerTest.java | 16 +++---- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index 63633afe..ba9b602f 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -13,19 +13,19 @@ These tools operate on Oracle JDBC thin client logs: -- **`get-stats`**: Extracts performance statistics including error counts, sent/received packets and byte counts. -- **`get-queries`**: Retrieves all executed SQL queries with timestamps and execution times. -- **`get-errors`**: Extracts all errors reported by both server and client. -- **`get-connections-events`**: Shows connection open/close events. +- **`get-jdbc-stats`**: Extracts performance statistics including error counts, sent/received packets and byte counts. +- **`get-jdbc-queries`**: Retrieves all executed SQL queries with timestamps and execution times. +- **`get-jdbc-errors`**: Extracts all errors reported by both server and client. +- **`get-jdbc-connections-events`**: Shows connection open/close events. - **`list-log-files-from-directory`**: List all visible files from a specified directory, which helps the user analyze multiple files with one prompt. -- **`log-comparison`**: Compares two log files for performance metrics, errors, and network information. +- **`jdbc-log-comparison`**: Compares two log files for performance metrics, errors, and network information. ### RDBMS/SQLNet Trace Analysis: These tools operate on RDBMS/SQLNet trace files: - **`get-rdbms-errors`**: Extracts errors from RDBMS/SQLNet trace files. -- **`get-packet-dumps`**: Extracts packet dumps for a specific connection ID. +- **`get-rdbms-packet-dumps`**: Extracts packet dumps for a specific connection ID. ### Vector Similarity Search @@ -107,7 +107,7 @@ This is the mode used by tools like Claude Desktop, where the client directly la "-Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service", "-Ddb.user=your_user", "-Ddb.password=your_password" - "-Dtools=get-stats,get-queries", + "-Dtools=get-jdbc-stats,get-jdbc-queries", "-Dojdbc.ext.dir=/path/to/extra-jars", "-jar", "/oracle-db-toolbox-mcp-server-1.0.0.jar" @@ -131,7 +131,7 @@ java \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ -Ddb.password=your_password \ - -Dtools=get-stats,get-queries \ + -Dtools=get-jdbc-stats,get-jdbc-queries \ -jar /oracle-db-toolbox-mcp-server-1.0.0.jar ``` This exposes the MCP endpoint at: `http://localhost:45450/mcp`. @@ -334,7 +334,7 @@ java -DconfigFile=/path/to/config.yaml -jar .jar Use * or all to enable everything. If omitted, all tools are enabled by default. - get-stats,get-queries + get-jdbc-stats,get-jdbc-queries ojdbc.ext.dir @@ -447,7 +447,7 @@ podman run --rm \ -Dhttps.port=45451 \ -DcertificatePath=[path/to/certificate] \ -DcertificatePassword=[password] \ - -Dtools=get-stats,get-queries \ + -Dtools=get-jdbc-stats,get-jdbc-queries \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ -Ddb.password=your_password" \ @@ -470,7 +470,7 @@ podman run --rm \ -Dtransport=http \ -Dhttp.port=45450 \ -Dhttps.port=45451 \ - -Dtools=get-stats,get-queries \ + -Dtools=get-jdbc-stats,get-jdbc-queries \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ -Ddb.password=your_password \ @@ -497,7 +497,7 @@ In this configuration, Claude Desktop runs `podman run --rm -i ... and connects "-i", "-v", "/absolute/path/to/ext:/ext:ro", "-e", - "JAVA_TOOL_OPTIONS=-Dtools=get-stats,get-queries -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service -Ddb.user=your_user -Ddb.password=your_password -Dojdbc.ext.dir=/ext -DconfigFile=/config/config.yaml", + "JAVA_TOOL_OPTIONS=-Dtools=get-jdbc-stats,get-jdbc-queries -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service -Ddb.user=your_user -Ddb.password=your_password -Dojdbc.ext.dir=/ext -DconfigFile=/config/config.yaml", "oracle-db-toolbox-mcp-server:1.0.0" ] } diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java index aa07cc60..3f504424 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java @@ -53,17 +53,17 @@ public static List getLogAnalyzerTools( /** *

* Builds and returns a {@link SyncToolSpecification SyncToolSpecification} - * for the {@code get-stats} tool, which retrieves high-level statistics from an Oracle JDBC thin log file. + * for the {@code get-jdbc-stats} tool, which retrieves high-level statistics from an Oracle JDBC thin log file. * The tool gathers information such as error count, the number of sent and * received packets, and byte counts from the specified log file. *

* - * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-stats} tool. + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-jdbc-stats} tool. */ private static SyncToolSpecification getStatsTool() { return SyncToolSpecification.builder() .tool(McpSchema.Tool.builder() - .name("get-stats") + .name("get-jdbc-stats") .title("Get JDBC Stats") .description("Return aggregated stats (error count, packets, bytes) from an Oracle JDBC thin log.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) @@ -81,17 +81,17 @@ private static SyncToolSpecification getStatsTool() { /** *

- * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-queries} tool. + * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-jdbc-queries} tool. * This tool extracts all executed SQL queries from an Oracle JDBC thin log file. * For each query, it provides the corresponding timestamp, execution time, connection id and tenant. *

* - * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-queries} tool. + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-jdbc-queries} tool. */ private static SyncToolSpecification getQueriesTool() { return SyncToolSpecification.builder() .tool(Tool.builder() - .name("get-queries") + .name("get-jdbc-queries") .title("Get JDBC Queries") .description("Get all executed queries from an Oracle JDBC thin log file, including the timestamp and execution time.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) @@ -113,17 +113,17 @@ private static SyncToolSpecification getQueriesTool() { /** *

- * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the get-errors tool. + * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the get-jdbc-errors tool. * This tool processes a specified Oracle JDBC thin log file and extracts all reported errors. * Each error record includes details such as the stack trace and additional log context. *

* ` - * @return a {@link SyncToolSpecification SyncToolSpecification} representing the get-errors tool. + * @return a {@link SyncToolSpecification SyncToolSpecification} representing the get-jdbc-errors tool. */ private static SyncToolSpecification getErrorsTool() { return SyncToolSpecification.builder() .tool(Tool.builder() - .name("get-errors") + .name("get-jdbc-errors") .title("Get JDBC Errors") .description("Get all reported errors from an Oracle JDBC thin log file, including stacktrace and log context.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) @@ -145,17 +145,17 @@ private static SyncToolSpecification getErrorsTool() { /** *

- * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-log-files-from-directory} tool. + * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code list-log-files-from-directory} tool. * This tool lists all Oracle JDBC log files present in the specified directory path. *

* - * @return a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-log-files-from-directory} tool. + * @return a {@link SyncToolSpecification SyncToolSpecification} for the {@code list-log-files-from-directory} tool. */ private static SyncToolSpecification getListLogsDirectoryTool() { return SyncToolSpecification.builder() .tool(Tool.builder() .name("list-log-files-from-directory") - .title("List Files From Directory") + .title("List Log Files From Directory") .description("List all visible files from a specified directory, which helps the user analyze multiple files with one prompt.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) .build()) @@ -181,17 +181,17 @@ private static SyncToolSpecification getListLogsDirectoryTool() { /** *

- * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code log-comparison} tool. + * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code jdbc-log-comparison} tool. * This tool enables comparison of two Oracle JDBC log files. It analyzes the specified log files and provides a JSON report * highlighting differences and similarities in performance metrics, encountered errors, and network-related information. *

* - * @return a {@link SyncToolSpecification SyncToolSpecification} instance that defines the {@code log-comparison} tool. + * @return a {@link SyncToolSpecification SyncToolSpecification} instance that defines the {@code jdbc-log-comparison} tool. */ private static SyncToolSpecification logComparisonTool() { return SyncToolSpecification.builder() .tool(Tool.builder() - .name("log-comparison") + .name("jdbc-log-comparison") .title("JDBC Log Comparison") .description("Compare two JDBC log files for performance metrics, errors, and network information.") .inputSchema(ToolSchemas.FILE_COMPARISON_SCHEMA) @@ -210,15 +210,15 @@ private static SyncToolSpecification logComparisonTool() { /** *

- * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-connection-events} tool. + * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the {@code get-jdbc-connection-events} tool. *

* - * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-connection-events} tool. + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-jdbc-connection-events} tool. */ private static SyncToolSpecification getConnectionEventsTool() { return SyncToolSpecification.builder() .tool(Tool.builder() - .name("get-connection-events") + .name("get-jdbc-connection-events") .title("Get JDBC Connection Events") .description("Retrieve opened and closed JDBC connection events from the log file with timestamp and connection details.") .inputSchema(ToolSchemas.FILE_PATH_SCHEMA) @@ -271,17 +271,17 @@ private static SyncToolSpecification getRdbmsErrorsTool() { /** *

- * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the get-packet-dumps tool. + * Builds and returns a {@link SyncToolSpecification SyncToolSpecification} for the get-rdbms-packet-dumps tool. * This tool extracts packet dump information from a specified RDBMS/SQLNet trace file that matches a given connection ID. * Each packet dump record includes its associated details and is serialized in JSON format. *

* - * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-packet-dumps} tool. + * @return a {@link SyncToolSpecification SyncToolSpecification} instance for the {@code get-rdbms-packet-dumps} tool. */ private static SyncToolSpecification getPacketDumpsTool() { return SyncToolSpecification.builder() .tool(Tool.builder() - .name("get-packet-dumps") + .name("get-rdbms-packet-dumps") .title("Get RDBMS/SQLNet Packet Dumps") .description("Extract packet dumps from RDBMS/SQLNet trace file for given connection ID.") .inputSchema(ToolSchemas.RDBMS_TOOLS_SCHEMA) diff --git a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java index b1a1f1f3..2524b8e2 100644 --- a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java +++ b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java @@ -28,14 +28,14 @@ static void initializeTools(){ @ParameterizedTest @ValueSource(strings = { - "get-stats", - "get-queries", - "get-errors", + "get-jdbc-stats", + "get-jdbc-queries", + "get-jdbc-errors", "list-log-files-from-directory", - "get-connection-events", - "log-comparison", + "get-jdbc-connection-events", + "jdbc-log-comparison", "get-rdbms-errors", - "get-packet-dumps" + "get-rdbms-packet-dumps" }) void testToolPresence(final String toolName) { final var isToolPresent = tools.containsKey(toolName); @@ -56,7 +56,7 @@ void testFilePathParameterInAllTools() { @Test void testSecondFilePathParameterInLogComparisonTool() { - final var toolProperties = tools.get("log-comparison") + final var toolProperties = tools.get("jdbc-log-comparison") .inputSchema() .properties(); @@ -72,7 +72,7 @@ void testSecondFilePathParameterInLogComparisonTool() { @Test void testConnectionIdParameterInGetPacketDumpsTool() { - final var toolProperties = tools.get("get-packet-dumps") + final var toolProperties = tools.get("get-rdbms-packet-dumps") .inputSchema() .properties(); From dcd6b3b3183d53b2e54c0f2277b783c024792a45 Mon Sep 17 00:00:00 2001 From: Jean de Lavarene Date: Wed, 10 Dec 2025 13:56:17 +0100 Subject: [PATCH 51/77] TODO list --- .../oracle/database/jdbc/OracleJDBCLogAnalyzer.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java index 3f504424..a0935bc4 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java @@ -4,7 +4,18 @@ ** Copyright (c) 2025 Oracle and/or its affiliates. ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ */ - +/* +TODO: + - Project name: Oracle Database MCP Toolkit + - Rename toolbox to toolkit everywhere + - Add copyright, author name (your name) to all files. + - Add UPL license to all files + - Add JavaDoc to all classes and most important methods. + - rename com.oracle.database.jdbc to com.oracle.database.mcptoolkit + - introduce a com.oracle.database.mcptoolkit.tools for the embedded tools (similarity search, log analyzer, etc.) + - rename OracleJDBCLogAnalyzer to LogAnalyzer + - update README to stress https, update the podman command + */ package com.oracle.database.jdbc; import com.oracle.database.jdbc.logs.model.JDBCConnectionEvent; From bf322920924030dd3772b53f43ff09f2b77ce8e2 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Wed, 10 Dec 2025 14:08:39 +0100 Subject: [PATCH 52/77] new tools package for embedded tools --- .../main/java/com/oracle/database/jdbc/ServerConfig.java | 2 +- .../src/main/java/com/oracle/database/jdbc/Utils.java | 9 ++++++--- .../jdbc/{ => tools}/ExplainAndExecutePlanTool.java | 5 +++-- .../database/jdbc/{ => tools}/OracleJDBCLogAnalyzer.java | 5 +++-- .../database/jdbc/{ => tools}/SimilaritySearchTool.java | 5 +++-- .../oracle/database/jdbc/{ => tools}/ToolSchemas.java | 2 +- .../oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java | 1 + 7 files changed, 18 insertions(+), 11 deletions(-) rename src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/{ => tools}/ExplainAndExecutePlanTool.java (98%) rename src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/{ => tools}/OracleJDBCLogAnalyzer.java (99%) rename src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/{ => tools}/SimilaritySearchTool.java (96%) rename src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/{ => tools}/ToolSchemas.java (98%) diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java index 2b65aaa2..f4855de0 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java @@ -28,7 +28,7 @@ * *

Use {@link #fromSystemProperties()} to create an instance with validation and defaults.

*/ -final class ServerConfig { +public final class ServerConfig { public final String dbUrl; public final String dbUser; public final String dbPassword; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java index a99be4a4..9beed265 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java @@ -5,6 +5,9 @@ import com.oracle.database.jdbc.config.SourceConfig; import com.oracle.database.jdbc.config.ToolConfig; import com.oracle.database.jdbc.config.ToolParameterConfig; +import com.oracle.database.jdbc.tools.ExplainAndExecutePlanTool; +import com.oracle.database.jdbc.tools.OracleJDBCLogAnalyzer; +import com.oracle.database.jdbc.tools.SimilaritySearchTool; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.spec.McpSchema; @@ -123,7 +126,7 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) } } - static String getOrDefault(Object v, String def) { + public static String getOrDefault(Object v, String def) { if (v == null) return def; String s = v.toString().trim(); return s.isEmpty() ? def : s; @@ -167,7 +170,7 @@ static ServerConfig loadConfig() { * @return open JDBC connection * @throws SQLException on acquisition failure */ - static Connection openConnection(ServerConfig cfg, String sourceName) throws SQLException { + public static Connection openConnection(ServerConfig cfg, String sourceName) throws SQLException { return getOrCreateDataSource(cfg, sourceName).getConnection(); } @@ -234,7 +237,7 @@ private static DataSource createDataSource(String url, String user, String passw * @param action The supplier action to execute, which may throw an {@link Exception} and returns a {@link McpSchema.CallToolResult}. * @return The result of the supplier if successful, or an error {@link McpSchema.CallToolResult} if an exception occurs. */ - static McpSchema.CallToolResult tryCall(ThrowingSupplier action) { + public static McpSchema.CallToolResult tryCall(ThrowingSupplier action) { try { return action.get(); } catch (Exception e) { diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ExplainAndExecutePlanTool.java similarity index 98% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java rename to src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ExplainAndExecutePlanTool.java index 7a217407..aa17367c 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ExplainAndExecutePlanTool.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ExplainAndExecutePlanTool.java @@ -1,5 +1,6 @@ -package com.oracle.database.jdbc; +package com.oracle.database.jdbc.tools; +import com.oracle.database.jdbc.ServerConfig; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.spec.McpSchema; @@ -20,7 +21,7 @@ import static com.oracle.database.jdbc.Utils.tryCall; public class ExplainAndExecutePlanTool { - static McpServerFeatures.SyncToolSpecification getExplainAndExecutePlanTool(ServerConfig config) { + public static McpServerFeatures.SyncToolSpecification getExplainAndExecutePlanTool(ServerConfig config) { return McpServerFeatures.SyncToolSpecification.builder() .tool(McpSchema.Tool.builder() diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/OracleJDBCLogAnalyzer.java similarity index 99% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java rename to src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/OracleJDBCLogAnalyzer.java index aa07cc60..b9f74b24 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzer.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/OracleJDBCLogAnalyzer.java @@ -5,8 +5,9 @@ ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ */ -package com.oracle.database.jdbc; +package com.oracle.database.jdbc.tools; +import com.oracle.database.jdbc.Utils; import com.oracle.database.jdbc.logs.model.JDBCConnectionEvent; import com.oracle.database.jdbc.logs.model.JDBCExecutedQuery; import com.oracle.database.jdbc.logs.model.LogError; @@ -309,7 +310,7 @@ private static SyncToolSpecification getPacketDumpsTool() { * @param the type of results supplied by this supplier */ @FunctionalInterface - interface ThrowingSupplier { + public interface ThrowingSupplier { /** * Gets a result, potentially throwing an {@link IOException}. * diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/SimilaritySearchTool.java similarity index 96% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java rename to src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/SimilaritySearchTool.java index 4f139533..ea9ee84c 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/SimilaritySearchTool.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/SimilaritySearchTool.java @@ -1,6 +1,7 @@ -package com.oracle.database.jdbc; +package com.oracle.database.jdbc.tools; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.oracle.database.jdbc.ServerConfig; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.spec.McpSchema; @@ -34,7 +35,7 @@ ORDER BY VECTOR_DISTANCE(%s, FETCH FIRST ? ROWS ONLY """; - static McpServerFeatures.SyncToolSpecification getSymilaritySearchTool(ServerConfig config) { + public static McpServerFeatures.SyncToolSpecification getSymilaritySearchTool(ServerConfig config) { return McpServerFeatures.SyncToolSpecification.builder() .tool(McpSchema.Tool.builder() diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ToolSchemas.java similarity index 98% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java rename to src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ToolSchemas.java index 712a92d1..948a1045 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ToolSchemas.java +++ b/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ToolSchemas.java @@ -1,4 +1,4 @@ -package com.oracle.database.jdbc; +package com.oracle.database.jdbc.tools; public class ToolSchemas { diff --git a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java index b1a1f1f3..ec7e1f6e 100644 --- a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java +++ b/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java @@ -1,5 +1,6 @@ package com.oracle.database.jdbc; +import com.oracle.database.jdbc.tools.OracleJDBCLogAnalyzer; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.spec.McpSchema; import org.junit.jupiter.api.BeforeAll; From 6475ef79df845ffcd84d4fea099d6a8f6acc648e Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Wed, 10 Dec 2025 15:12:27 +0100 Subject: [PATCH 53/77] Updating read.me to put more emphasis on custom tools and ssl support --- src/oracle-db-toolbox-mcp-server/README.md | 114 ++++++++++++++------- 1 file changed, 79 insertions(+), 35 deletions(-) diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-toolbox-mcp-server/README.md index ba9b602f..303a2343 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-toolbox-mcp-server/README.md @@ -71,6 +71,56 @@ These tools operate on RDBMS/SQLNet trace files: * `llmPrompt`: A structured prompt for an LLM to explain + tune the plan. --- + +## Custom Tool Framework — Extending the MCP Server +The MCP server can load both database connection definitions and custom tool definitions from a YAML configuration file. +This provides a flexible and declarative way to extend the server without modifying or rebuilding the codebase. + +A YAML file may define: + + * One or more **sources:** — named database configurations (URL, user, password, etc.) + + * One or more **tools** — each with parameters, SQL statements, and optional metadata + +### Source Resolution Logic + +When executing a tool, the MCP server determines which source to use based on the following rules: + +1. If the tool specifies a source, that source is used. + +2. If the tool does not specify a source, the server looks for a default source: + + * First, it checks whether a source was provided via system properties (db.url, db.user, db.password) (Higher priority). + + * If no system property source is available, it falls back to the first source defined in the YAML file, if present. + +3. If no source can be resolved and the tool requires one (e.g., SQL-based tools), the server reports a configuration error. + +This design ensures that tools always have a predictable source while giving you flexibility to choose how connections are provided—either inline in YAML or externally via system properties and environment variables. + +**Example `config.yaml`:** +```yaml +sources: + prod-db: + url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 + user: ADMIN + password: ${password} + +tools: + hotels-by-name: + source: prod-db + parameters: + - name: name + type: string + description: Hotel name to search for. + required: false + statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' +``` +To enable YAML configuration, launch the server with: +```bash +java -DconfigFile=/path/to/config.yaml -jar .jar +``` + ## Prerequisites - **Java 17+** (JDK) @@ -136,13 +186,13 @@ java \ ``` This exposes the MCP endpoint at: `http://localhost:45450/mcp`. -### Note -You can enable HTTPS (SSL/TLS) by specifying the path to your certificate keystore and its password using the -DcertificatePath and -DcertificatePassword options. -Only PKCS12 (.p12 or .pfx) keystore format is supported. -You can also change the HTTPS port with the -DhttpsPort option (default is 45451). +#### Enabling HTTPS (SSL/TLS) +To enable HTTPS (SSL/TLS), specify your certificate keystore path and password using the `-DcertificatePath` and `-DcertificatePassword` options. +Only PKCS12 (`.p12` or `.pfx`) keystore files are supported. +You can set the HTTPS port with the `-Dhttps.port` option. ##### Example ```shell --DcertificatePath=/path/to/your-certificate.p12 -DcertificatePassword=yourPassword -DhttpsPort=443 +-DcertificatePath=/path/to/your-certificate.p12 -DcertificatePassword=yourPassword -Dhttps.port=443 ``` ### Using HTTP from Cline Cline supports streamable HTTP directly. Example: @@ -268,35 +318,6 @@ INFO: Authorization token generated (for testing and development use only): Secr Ultimately, the token must be included in the http request header (e.g. `Authorization: Bearer 0dd11948-37a3-470f-911e-4cd8b3d6f69c` or `Authorization: Bearer Secret_DeV_T0ken`). -## YAML Configuration Support - -The MCP server supports loading database connection and tool definitions from a YAML configuration file. -For sources, if a tool has a specific source it will use it. Otherwise, it will look for the default source which is either the source we got from system properties, otherwise, the first source defined in the file (if any). -This file can contain environment variables as well. - -**Example `config.yaml`:** -```yaml -sources: - prod-db: - url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 - user: ADMIN - password: ${password} - -tools: - hotels-by-name: - source: prod-db - parameters: - - name: name - type: string - description: Hotel name to search for. - required: false - statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' -``` -To enable YAML configuration, launch the server with: -```bash -java -DconfigFile=/path/to/config.yaml -jar .jar -``` - ### Supported System Properties @@ -362,6 +383,29 @@ java -DconfigFile=/path/to/config.yaml -jar .jar + + + + + + + + + + + + + + + + + @@ -434,7 +478,7 @@ From the project root (where the Dockerfile lives): podman build -t oracle-db-toolbox-mcp-server:1.0.0 . ``` ### Run the container (HTTP mode example) -This example runs the MCP server over HTTP inside the container and exposes it on port 45450 on your host. +This example runs the MCP server over HTTP and HTTPS inside the container and exposes it on port 45450 and 45451 on your host. ```bash podman run --rm \ From ce65927afe3d77beef690133f4667a5c80f22141 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 10 Dec 2025 17:13:06 +0100 Subject: [PATCH 54/77] Rename to package --- .../Dockerfile | 0 .../README.md | 4 ++-- .../pom.xml | 12 ++++++------ .../database/mcptoolkit}/EnvSubstitutor.java | 2 +- .../database/mcptoolkit}/LoadedConstants.java | 2 +- .../mcptoolkit}/OracleDBToolboxMCPServer.java | 14 +++++++------- .../database/mcptoolkit}/ServerConfig.java | 8 ++++---- .../com/oracle/database/mcptoolkit}/Utils.java | 16 ++++++++-------- .../database/mcptoolkit}/config/ConfigRoot.java | 2 +- .../mcptoolkit}/config/SourceConfig.java | 4 ++-- .../database/mcptoolkit}/config/ToolConfig.java | 4 ++-- .../mcptoolkit}/config/ToolParameterConfig.java | 4 ++-- .../mcptoolkit}/oauth/OAuth2Configuration.java | 4 ++-- .../mcptoolkit}/oauth/OAuth2TokenValidator.java | 2 +- .../mcptoolkit}/oauth/TokenGenerator.java | 4 ++-- .../tools/ExplainAndExecutePlanTool.java | 8 ++++---- .../mcptoolkit}/tools/OracleJDBCLogAnalyzer.java | 4 ++-- .../mcptoolkit}/tools/SimilaritySearchTool.java | 10 +++++----- .../database/mcptoolkit}/tools/ToolSchemas.java | 2 +- .../mcptoolkit}/web/AuthorizationFilter.java | 6 +++--- .../web/RedirectOAuthToOpenIDServlet.java | 4 ++-- .../database/mcptoolkit}/web/WebUtils.java | 4 ++-- .../mcptoolkit}/web/WellKnownServlet.java | 4 ++-- .../mcptoolkit}/OracleJDBCLogAnalyzerTest.java | 4 ++-- 24 files changed, 64 insertions(+), 64 deletions(-) rename src/{oracle-db-toolbox-mcp-server => oracle-db-mcp-toolkit}/Dockerfile (100%) rename src/{oracle-db-toolbox-mcp-server => oracle-db-mcp-toolkit}/README.md (99%) rename src/{oracle-db-toolbox-mcp-server => oracle-db-mcp-toolkit}/pom.xml (86%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/EnvSubstitutor.java (94%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/LoadedConstants.java (97%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/OracleDBToolboxMCPServer.java (93%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/ServerConfig.java (96%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/Utils.java (96%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/config/ConfigRoot.java (90%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/config/SourceConfig.java (88%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/config/ToolConfig.java (94%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/config/ToolParameterConfig.java (79%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/oauth/OAuth2Configuration.java (97%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/oauth/OAuth2TokenValidator.java (98%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/oauth/TokenGenerator.java (93%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/tools/ExplainAndExecutePlanTool.java (97%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/tools/OracleJDBCLogAnalyzer.java (99%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/tools/SimilaritySearchTool.java (95%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/tools/ToolSchemas.java (98%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/web/AuthorizationFilter.java (92%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/web/RedirectOAuthToOpenIDServlet.java (85%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/web/WebUtils.java (96%) rename src/{oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit}/web/WellKnownServlet.java (92%) rename src/{oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc => oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit}/OracleJDBCLogAnalyzerTest.java (96%) diff --git a/src/oracle-db-toolbox-mcp-server/Dockerfile b/src/oracle-db-mcp-toolkit/Dockerfile similarity index 100% rename from src/oracle-db-toolbox-mcp-server/Dockerfile rename to src/oracle-db-mcp-toolkit/Dockerfile diff --git a/src/oracle-db-toolbox-mcp-server/README.md b/src/oracle-db-mcp-toolkit/README.md similarity index 99% rename from src/oracle-db-toolbox-mcp-server/README.md rename to src/oracle-db-mcp-toolkit/README.md index 303a2343..66f3dae9 100644 --- a/src/oracle-db-toolbox-mcp-server/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -1,8 +1,8 @@ -# Oracle DB Toolbox MCP Server +# Oracle Database MCP Toolkit ## Overview -`oracle-db-toolbox-mcp-server` is a Model Context Protocol (MCP) server that lets you: +Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that lets you: * Use 8 built-in tools to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. * Optionally use **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**, when JDBC configuration is provided. * Define your own custom tools via a simple YAML configuration file. diff --git a/src/oracle-db-toolbox-mcp-server/pom.xml b/src/oracle-db-mcp-toolkit/pom.xml similarity index 86% rename from src/oracle-db-toolbox-mcp-server/pom.xml rename to src/oracle-db-mcp-toolkit/pom.xml index 766d42c1..d3707c86 100644 --- a/src/oracle-db-toolbox-mcp-server/pom.xml +++ b/src/oracle-db-mcp-toolkit/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.oracle.database.jdbc - oracle-db-toolbox-mcp-server + com.oracle.database.mcptoolkit + oracle-db-mcp-toolkit 1.0.0 jar Oracle JDBC Log Analyzer MCP Server - The Oracle JDBC Log Analyzer MCP Server provides tools for analyzing - Oracle JDBC thin client logs and RDBMS/SQLNet trace files. - https://github.com/Youssef-Erradi/mcp/tree/main/src/ojdbc-log-analyzer-mcp-server + The Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that enables users to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files using built-in tools. + It also offers database-powered tools, such as vector similarity search and SQL execution plan analysis. Additionally, users can define custom tools through a YAML configuration file. + https://github.com/oracle/mcp/tree/main/src/oracle-db-mcp-toolkit @@ -103,7 +103,7 @@ false - com.oracle.database.jdbc.OracleDBToolboxMCPServer + com.oracle.database.mcptoolkit.OracleDBToolboxMCPServer diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/EnvSubstitutor.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java similarity index 94% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/EnvSubstitutor.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java index 786e5f64..c9ca4820 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/EnvSubstitutor.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java @@ -1,4 +1,4 @@ -package com.oracle.database.jdbc; +package com.oracle.database.mcptoolkit; import java.util.regex.*; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java similarity index 97% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java index d279a388..d8bf21d3 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/LoadedConstants.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java @@ -1,4 +1,4 @@ -package com.oracle.database.jdbc; +package com.oracle.database.mcptoolkit; public final class LoadedConstants { private LoadedConstants() {} // Prevent instantiation diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDBToolboxMCPServer.java similarity index 93% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDBToolboxMCPServer.java index 8cd32dc5..10eee8f1 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDBToolboxMCPServer.java @@ -1,11 +1,11 @@ -package com.oracle.database.jdbc; +package com.oracle.database.mcptoolkit; import com.fasterxml.jackson.databind.ObjectMapper; -import com.oracle.database.jdbc.oauth.OAuth2Configuration; -import com.oracle.database.jdbc.web.AuthorizationFilter; -import com.oracle.database.jdbc.web.RedirectOAuthToOpenIDServlet; -import com.oracle.database.jdbc.web.WebUtils; -import com.oracle.database.jdbc.web.WellKnownServlet; +import com.oracle.database.mcptoolkit.oauth.OAuth2Configuration; +import com.oracle.database.mcptoolkit.web.AuthorizationFilter; +import com.oracle.database.mcptoolkit.web.RedirectOAuthToOpenIDServlet; +import com.oracle.database.mcptoolkit.web.WebUtils; +import com.oracle.database.mcptoolkit.web.WellKnownServlet; import io.modelcontextprotocol.server.McpServer; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; @@ -23,7 +23,7 @@ import java.io.File; import java.util.logging.Logger; -import static com.oracle.database.jdbc.Utils.installExternalExtensionsFromDir; +import static com.oracle.database.mcptoolkit.Utils.installExternalExtensionsFromDir; public class OracleDBToolboxMCPServer { private static final Logger LOG = Logger.getLogger(OracleDBToolboxMCPServer.class.getName()); diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java similarity index 96% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java index f4855de0..4864ba78 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/ServerConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java @@ -1,8 +1,8 @@ -package com.oracle.database.jdbc; +package com.oracle.database.mcptoolkit; -import com.oracle.database.jdbc.config.ConfigRoot; -import com.oracle.database.jdbc.config.SourceConfig; -import com.oracle.database.jdbc.config.ToolConfig; +import com.oracle.database.mcptoolkit.config.ConfigRoot; +import com.oracle.database.mcptoolkit.config.SourceConfig; +import com.oracle.database.mcptoolkit.config.ToolConfig; import java.util.Collections; import java.util.HashMap; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java similarity index 96% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index 9beed265..87116d90 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -1,13 +1,13 @@ -package com.oracle.database.jdbc; +package com.oracle.database.mcptoolkit; import com.fasterxml.jackson.databind.ObjectMapper; -import com.oracle.database.jdbc.config.ConfigRoot; -import com.oracle.database.jdbc.config.SourceConfig; -import com.oracle.database.jdbc.config.ToolConfig; -import com.oracle.database.jdbc.config.ToolParameterConfig; -import com.oracle.database.jdbc.tools.ExplainAndExecutePlanTool; -import com.oracle.database.jdbc.tools.OracleJDBCLogAnalyzer; -import com.oracle.database.jdbc.tools.SimilaritySearchTool; +import com.oracle.database.mcptoolkit.config.ConfigRoot; +import com.oracle.database.mcptoolkit.config.SourceConfig; +import com.oracle.database.mcptoolkit.config.ToolConfig; +import com.oracle.database.mcptoolkit.config.ToolParameterConfig; +import com.oracle.database.mcptoolkit.tools.ExplainAndExecutePlanTool; +import com.oracle.database.mcptoolkit.tools.OracleJDBCLogAnalyzer; +import com.oracle.database.mcptoolkit.tools.SimilaritySearchTool; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.spec.McpSchema; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java similarity index 90% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java index b22bacd7..f173a5ce 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ConfigRoot.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java @@ -1,4 +1,4 @@ -package com.oracle.database.jdbc.config; +package com.oracle.database.mcptoolkit.config; import java.util.Map; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java similarity index 88% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java index 8bcf2632..ae1c1858 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/SourceConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.config; +package com.oracle.database.mcptoolkit.config; -import com.oracle.database.jdbc.EnvSubstitutor; +import com.oracle.database.mcptoolkit.EnvSubstitutor; public class SourceConfig { public String host; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java similarity index 94% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java index 61076a0c..86edf5ba 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java @@ -1,9 +1,9 @@ -package com.oracle.database.jdbc.config; +package com.oracle.database.mcptoolkit.config; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.oracle.database.jdbc.EnvSubstitutor; +import com.oracle.database.mcptoolkit.EnvSubstitutor; import java.util.List; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java similarity index 79% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java index b993a325..2d81066a 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/config/ToolParameterConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.config; +package com.oracle.database.mcptoolkit.config; -import com.oracle.database.jdbc.EnvSubstitutor; +import com.oracle.database.mcptoolkit.EnvSubstitutor; public class ToolParameterConfig { public String name; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2Configuration.java similarity index 97% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2Configuration.java index 53ccd8ac..db9a70d0 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2Configuration.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2Configuration.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.oauth; +package com.oracle.database.mcptoolkit.oauth; -import com.oracle.database.jdbc.LoadedConstants; +import com.oracle.database.mcptoolkit.LoadedConstants; import java.util.ArrayList; import java.util.List; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2TokenValidator.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2TokenValidator.java similarity index 98% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2TokenValidator.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2TokenValidator.java index 57e16766..53c9f3b3 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/OAuth2TokenValidator.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2TokenValidator.java @@ -1,4 +1,4 @@ -package com.oracle.database.jdbc.oauth; +package com.oracle.database.mcptoolkit.oauth; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletResponse; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java similarity index 93% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java index 4425ad58..4e282882 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/oauth/TokenGenerator.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.oauth; +package com.oracle.database.mcptoolkit.oauth; -import static com.oracle.database.jdbc.LoadedConstants.ORACLE_DB_TOOLBOX_AUTH_TOKEN; +import static com.oracle.database.mcptoolkit.LoadedConstants.ORACLE_DB_TOOLBOX_AUTH_TOKEN; import java.util.UUID; import java.util.logging.Level; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ExplainAndExecutePlanTool.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ExplainAndExecutePlanTool.java similarity index 97% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ExplainAndExecutePlanTool.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ExplainAndExecutePlanTool.java index aa17367c..9bb26d28 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ExplainAndExecutePlanTool.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ExplainAndExecutePlanTool.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.tools; +package com.oracle.database.mcptoolkit.tools; -import com.oracle.database.jdbc.ServerConfig; +import com.oracle.database.mcptoolkit.ServerConfig; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.spec.McpSchema; @@ -17,8 +17,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.oracle.database.jdbc.Utils.openConnection; -import static com.oracle.database.jdbc.Utils.tryCall; +import static com.oracle.database.mcptoolkit.Utils.openConnection; +import static com.oracle.database.mcptoolkit.Utils.tryCall; public class ExplainAndExecutePlanTool { public static McpServerFeatures.SyncToolSpecification getExplainAndExecutePlanTool(ServerConfig config) { diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/OracleJDBCLogAnalyzer.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/OracleJDBCLogAnalyzer.java similarity index 99% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/OracleJDBCLogAnalyzer.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/OracleJDBCLogAnalyzer.java index 514533cf..43a21429 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/OracleJDBCLogAnalyzer.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/OracleJDBCLogAnalyzer.java @@ -5,9 +5,9 @@ ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ */ -package com.oracle.database.jdbc.tools; +package com.oracle.database.mcptoolkit.tools; -import com.oracle.database.jdbc.Utils; +import com.oracle.database.mcptoolkit.Utils; import com.oracle.database.jdbc.logs.model.JDBCConnectionEvent; import com.oracle.database.jdbc.logs.model.JDBCExecutedQuery; import com.oracle.database.jdbc.logs.model.LogError; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/SimilaritySearchTool.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/SimilaritySearchTool.java similarity index 95% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/SimilaritySearchTool.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/SimilaritySearchTool.java index ea9ee84c..68049ccf 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/SimilaritySearchTool.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/SimilaritySearchTool.java @@ -1,7 +1,7 @@ -package com.oracle.database.jdbc.tools; +package com.oracle.database.mcptoolkit.tools; import com.fasterxml.jackson.databind.json.JsonMapper; -import com.oracle.database.jdbc.ServerConfig; +import com.oracle.database.mcptoolkit.ServerConfig; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.spec.McpSchema; @@ -14,9 +14,9 @@ import java.util.Map; import java.util.regex.Pattern; -import static com.oracle.database.jdbc.Utils.openConnection; -import static com.oracle.database.jdbc.Utils.tryCall; -import static com.oracle.database.jdbc.Utils.getOrDefault; +import static com.oracle.database.mcptoolkit.Utils.openConnection; +import static com.oracle.database.mcptoolkit.Utils.tryCall; +import static com.oracle.database.mcptoolkit.Utils.getOrDefault; public class SimilaritySearchTool { diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ToolSchemas.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ToolSchemas.java similarity index 98% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ToolSchemas.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ToolSchemas.java index 948a1045..9bb03ce6 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/tools/ToolSchemas.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ToolSchemas.java @@ -1,4 +1,4 @@ -package com.oracle.database.jdbc.tools; +package com.oracle.database.mcptoolkit.tools; public class ToolSchemas { diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/AuthorizationFilter.java similarity index 92% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/AuthorizationFilter.java index e23c510b..164caf7b 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/AuthorizationFilter.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/AuthorizationFilter.java @@ -1,7 +1,7 @@ -package com.oracle.database.jdbc.web; +package com.oracle.database.mcptoolkit.web; -import com.oracle.database.jdbc.oauth.OAuth2Configuration; -import com.oracle.database.jdbc.oauth.OAuth2TokenValidator; +import com.oracle.database.mcptoolkit.oauth.OAuth2Configuration; +import com.oracle.database.mcptoolkit.oauth.OAuth2TokenValidator; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/RedirectOAuthToOpenIDServlet.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/RedirectOAuthToOpenIDServlet.java similarity index 85% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/RedirectOAuthToOpenIDServlet.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/RedirectOAuthToOpenIDServlet.java index 70db4859..355ab562 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/RedirectOAuthToOpenIDServlet.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/RedirectOAuthToOpenIDServlet.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.web; +package com.oracle.database.mcptoolkit.web; -import com.oracle.database.jdbc.oauth.OAuth2Configuration; +import com.oracle.database.mcptoolkit.oauth.OAuth2Configuration; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WebUtils.java similarity index 96% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WebUtils.java index 0802955c..04749d1b 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WebUtils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WebUtils.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.web; +package com.oracle.database.mcptoolkit.web; -import com.oracle.database.jdbc.LoadedConstants; +import com.oracle.database.mcptoolkit.LoadedConstants; import jakarta.servlet.http.HttpServletRequest; import java.util.Objects; diff --git a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WellKnownServlet.java similarity index 92% rename from src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WellKnownServlet.java index 6103f308..d37d22e5 100644 --- a/src/oracle-db-toolbox-mcp-server/src/main/java/com/oracle/database/jdbc/web/WellKnownServlet.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WellKnownServlet.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc.web; +package com.oracle.database.mcptoolkit.web; -import com.oracle.database.jdbc.oauth.OAuth2Configuration; +import com.oracle.database.mcptoolkit.oauth.OAuth2Configuration; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java b/src/oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit/OracleJDBCLogAnalyzerTest.java similarity index 96% rename from src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java rename to src/oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit/OracleJDBCLogAnalyzerTest.java index d664c224..752d6948 100644 --- a/src/oracle-db-toolbox-mcp-server/src/test/java/com/oracle/database/jdbc/OracleJDBCLogAnalyzerTest.java +++ b/src/oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit/OracleJDBCLogAnalyzerTest.java @@ -1,6 +1,6 @@ -package com.oracle.database.jdbc; +package com.oracle.database.mcptoolkit; -import com.oracle.database.jdbc.tools.OracleJDBCLogAnalyzer; +import com.oracle.database.mcptoolkit.tools.OracleJDBCLogAnalyzer; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.spec.McpSchema; import org.junit.jupiter.api.BeforeAll; From 026dbf9056e0cbcfd559ae1695eec70cfdfd9834 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 10 Dec 2025 17:42:49 +0100 Subject: [PATCH 55/77] Add Copyright and License headders + Add JavaDoc --- .../database/mcptoolkit/EnvSubstitutor.java | 22 +++++++++ .../database/mcptoolkit/LoadedConstants.java | 14 ++++++ ...ver.java => OracleDatabaseMCPToolkit.java} | 10 ++-- .../database/mcptoolkit/ServerConfig.java | 7 +++ .../com/oracle/database/mcptoolkit/Utils.java | 13 +++-- .../mcptoolkit/config/ConfigRoot.java | 16 ++++++ .../mcptoolkit/config/SourceConfig.java | 49 ++++++++++++++++++- .../mcptoolkit/config/ToolConfig.java | 41 ++++++++++++++-- .../config/ToolParameterConfig.java | 32 ++++++++++++ .../mcptoolkit/oauth/OAuth2Configuration.java | 8 +++ .../oauth/OAuth2TokenValidator.java | 7 +++ .../mcptoolkit/oauth/TokenGenerator.java | 7 +++ .../tools/ExplainAndExecutePlanTool.java | 21 +++++++- ...LogAnalyzer.java => LogAnalyzerTools.java} | 24 +++++++-- .../tools/SimilaritySearchTool.java | 21 ++++++++ .../mcptoolkit/tools/ToolSchemas.java | 45 +++++++++++++++++ .../mcptoolkit/web/AuthorizationFilter.java | 44 +++++++++++++++++ .../web/RedirectOAuthToOpenIDServlet.java | 25 ++++++++++ .../database/mcptoolkit/web/WebUtils.java | 7 +++ .../mcptoolkit/web/WellKnownServlet.java | 26 ++++++++++ .../mcptoolkit/OracleJDBCLogAnalyzerTest.java | 4 +- 21 files changed, 427 insertions(+), 16 deletions(-) rename src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/{OracleDBToolboxMCPServer.java => OracleDatabaseMCPToolkit.java} (94%) rename src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/{OracleJDBCLogAnalyzer.java => LogAnalyzerTools.java} (90%) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java index c9ca4820..44635dc2 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java @@ -1,10 +1,32 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit; import java.util.regex.*; +/** + * The EnvSubstitutor class provides a method for substituting environment variables in a given string. + * It replaces placeholders in the format ${VARIABLE_NAME} with the corresponding environment variable values. + */ public class EnvSubstitutor { + /** + * Pattern used to match placeholders in the input string. + * The pattern matches strings in the format ${VARIABLE_NAME}. + */ private static final Pattern PLACEHOLDER = Pattern.compile("\\$\\{([^}]+)}"); + /** + * Substitutes environment variables in the given input string. + * + * @param input the input string containing placeholders for environment variables + * @return the input string with environment variables substituted + * @throws IllegalStateException if an environment variable is not set + */ public static String substituteEnvVars(String input) { if (input == null) return null; Matcher m = PLACEHOLDER.matcher(input); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java index d8bf21d3..52b96cbd 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java @@ -1,5 +1,19 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit; +/** + * Provides a set of constants loaded from system properties and environment variables. + * These constants are used to configure various aspects of the application, including + * network settings, tool configurations, OAuth settings, and more. + * + *

This class is not intended to be instantiated and provides only static constants. + */ public final class LoadedConstants { private LoadedConstants() {} // Prevent instantiation diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDBToolboxMCPServer.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java similarity index 94% rename from src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDBToolboxMCPServer.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java index 10eee8f1..fedb3cf2 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDBToolboxMCPServer.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java @@ -25,8 +25,12 @@ import static com.oracle.database.mcptoolkit.Utils.installExternalExtensionsFromDir; -public class OracleDBToolboxMCPServer { - private static final Logger LOG = Logger.getLogger(OracleDBToolboxMCPServer.class.getName()); +/** + * The OracleDatabaseMCPToolkit class provides the main entry point for the MCP server. + * It initializes the configuration, sets up the transport layer, and starts the MCP server. + */ +public class OracleDatabaseMCPToolkit { + private static final Logger LOG = Logger.getLogger(OracleDatabaseMCPToolkit.class.getName()); static ServerConfig config; @@ -60,7 +64,7 @@ public static void main(String[] args) { Utils.addSyncToolSpecifications(server, config); } - private OracleDBToolboxMCPServer() { + private OracleDatabaseMCPToolkit() { // Prevent instantiation } diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java index 4864ba78..16292a21 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit; import com.oracle.database.mcptoolkit.config.ConfigRoot; diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index 87116d90..6c05e3c0 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit; import com.fasterxml.jackson.databind.ObjectMapper; @@ -6,7 +13,7 @@ import com.oracle.database.mcptoolkit.config.ToolConfig; import com.oracle.database.mcptoolkit.config.ToolParameterConfig; import com.oracle.database.mcptoolkit.tools.ExplainAndExecutePlanTool; -import com.oracle.database.mcptoolkit.tools.OracleJDBCLogAnalyzer; +import com.oracle.database.mcptoolkit.tools.LogAnalyzerTools; import com.oracle.database.mcptoolkit.tools.SimilaritySearchTool; import io.modelcontextprotocol.server.McpServerFeatures; import io.modelcontextprotocol.server.McpSyncServer; @@ -63,7 +70,7 @@ public class Utils { *

*/ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) { - List specs = OracleJDBCLogAnalyzer.getLogAnalyzerTools(); + List specs = LogAnalyzerTools.getTools(); for (McpServerFeatures.SyncToolSpecification spec : specs) { String toolName = spec.tool().name(); // e.g. "get-stats", "get-queries" if (isToolEnabled(config, toolName)) { @@ -221,7 +228,7 @@ private static DataSource createDataSource(String url, String user, String passw /** *

- * Executes the provided {@link OracleJDBCLogAnalyzer.ThrowingSupplier ThrowingSupplier} action, + * Executes the provided {@link LogAnalyzerTools.ThrowingSupplier ThrowingSupplier} action, * which may throw an {@link Exception}, and returns the resulting {@link McpSchema.CallToolResult}. *
* If the action executes successfully, its {@link McpSchema.CallToolResult} is returned as-is. diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java index f173a5ce..27927e7a 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java @@ -1,11 +1,27 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.config; import java.util.Map; +/** + * Represents the root configuration for the application, containing a map of source configurations and tool configurations. + */ public class ConfigRoot { public Map sources; public Map tools; + /** + * Substitutes environment variables in the source and tool configurations. + *

+ * This method iterates over the source and tool configurations, substituting environment variables + * in each configuration's fields. + */ public void substituteEnvVars() { if (sources != null) { for (SourceConfig sc : sources.values()) { diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java index ae1c1858..624f3f63 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java @@ -1,15 +1,57 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.config; import com.oracle.database.mcptoolkit.EnvSubstitutor; +/** + * Represents the configuration for a data source, specifically for an Oracle database. + *

+ * This class encapsulates the necessary properties to establish a connection to the database. + */ public class SourceConfig { + /** + * The hostname or IP address of the database server. + */ public String host; + + /** + * The port number on which the database server is listening. + */ public String port; - public String database; // Oracle SID or service name + + /** + * The Oracle SID or service name of the database. + */ + public String database; + + /** + * The JDBC URL for the database connection. If not provided, it will be constructed + * using the host, port, and database properties. + */ public String url; + + /** + * The username to use for the database connection. + */ public String user; + + /** + * The password to use for the database connection. + */ public String password; + /** + * Returns the JDBC URL for the database connection. If the {@link #url} property is not set, + * it will be constructed using the {@link #host}, {@link #port}, and {@link #database} properties. + * + * @return the JDBC URL for the database connection + */ public String toJdbcUrl() { if(url == null) { return String.format("jdbc:oracle:thin:@%s:%s/%s", host, port, database); @@ -18,6 +60,11 @@ public String toJdbcUrl() { } } + /** + * Substitutes environment variables in the configuration properties. + *

+ * This method replaces placeholders in the form of ${VARIABLE_NAME} with the actual environment variable values. + */ public void substituteEnvVars() { this.host = EnvSubstitutor.substituteEnvVars(this.host); this.port = EnvSubstitutor.substituteEnvVars(this.port); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java index 86edf5ba..8c6acdd0 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.config; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -7,13 +14,41 @@ import java.util.List; +/** + * Represents a tool configuration, encapsulating its properties and behavior. + */ public class ToolConfig { - public String name; // The tool name (from YAML key) - public String source; // Reference key from sources + /** + * The tool name, derived from the YAML key. + */ + public String name; + + /** + * Reference key from sources. + */ + public String source; + + /** + * A brief description of the tool. + */ public String description; + + /** + * A list of parameter configurations for the tool. + */ public List parameters; - public String statement; // The SQL statement to execute + /** + * The SQL statement to be executed by the tool. + */ + public String statement; + + /** + * Substitutes environment variables in the tool configuration. + *

+ * Replaces placeholders in the tool's name, source, description, and statement with their corresponding environment variable values. + * Also substitutes environment variables in the tool's parameters, if any. + */ public void substituteEnvVars() { this.name = EnvSubstitutor.substituteEnvVars(this.name); this.source = EnvSubstitutor.substituteEnvVars(this.source); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java index 2d81066a..8573590d 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java @@ -1,13 +1,45 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.config; import com.oracle.database.mcptoolkit.EnvSubstitutor; +/** + * Represents a configuration for a tool parameter. + *

+ * This class encapsulates the properties of a tool parameter, including its name, type, description, and whether it is required. + */ public class ToolParameterConfig { + /** + * The name of the tool parameter. + */ public String name; + + /** + * The data type of the tool parameter. + */ public String type; + + /** + * A human-readable description of the tool parameter. + */ public String description; + + /** + * Indicates whether the tool parameter is required. + */ public boolean required; + /** + * Substitutes environment variables in the tool parameter's properties. + *

+ * This method replaces any environment variable references in the {@link #name}, {@link #type}, and {@link #description} fields with their corresponding values. + */ public void substituteEnvVars() { this.name = EnvSubstitutor.substituteEnvVars(this.name); this.type = EnvSubstitutor.substituteEnvVars(this.type); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2Configuration.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2Configuration.java index db9a70d0..5aa82ab9 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2Configuration.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2Configuration.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.oauth; import com.oracle.database.mcptoolkit.LoadedConstants; @@ -9,6 +16,7 @@ /** * The OAuth2Configuration class is a singleton that manages OAuth2 authentication configuration settings. * It reads configuration values from system properties and provides access to them via getter methods. + *

* This class also handles logging based on whether authentication and OAuth2 are enabled or configured. * If OAuth2 is not properly configured, it initializes a TokenGenerator for local token generation. */ diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2TokenValidator.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2TokenValidator.java index 53c9f3b3..f6eac068 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2TokenValidator.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/OAuth2TokenValidator.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.oauth; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java index 4e282882..0e4a02d4 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.oauth; import static com.oracle.database.mcptoolkit.LoadedConstants.ORACLE_DB_TOOLBOX_AUTH_TOKEN; diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ExplainAndExecutePlanTool.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ExplainAndExecutePlanTool.java index 9bb26d28..42ad06fa 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ExplainAndExecutePlanTool.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ExplainAndExecutePlanTool.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.tools; import com.oracle.database.mcptoolkit.ServerConfig; @@ -20,7 +27,20 @@ import static com.oracle.database.mcptoolkit.Utils.openConnection; import static com.oracle.database.mcptoolkit.Utils.tryCall; +/** + * Provides functionality for explaining and executing Oracle SQL plans. + * This class contains methods to generate execution plans for SQL queries and + * to explain these plans in a human-readable format. + */ public class ExplainAndExecutePlanTool { + /** + * Returns a tool specification for the "explain_plan" tool. + * This tool generates an Oracle execution plan for the provided SQL and + * produces an accompanying LLM prompt to explain and tune the plan. + * + * @param config Server configuration + * @return Tool specification for the "explain_plan" tool + */ public static McpServerFeatures.SyncToolSpecification getExplainAndExecutePlanTool(ServerConfig config) { return McpServerFeatures.SyncToolSpecification.builder() @@ -103,7 +123,6 @@ public static McpServerFeatures.SyncToolSpecification getExplainAndExecutePlanTo * @param execute whether to execute or just parse (null = auto per SQL type) * @param xplanOptions DBMS_XPLAN formatting options */ - static ExplainResult getExplainPlan( Connection c, String sql, diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/OracleJDBCLogAnalyzer.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/LogAnalyzerTools.java similarity index 90% rename from src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/OracleJDBCLogAnalyzer.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/LogAnalyzerTools.java index 43a21429..b6373e59 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/OracleJDBCLogAnalyzer.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/LogAnalyzerTools.java @@ -1,5 +1,5 @@ /* - ** Oracle JDBC Log Analyzer MCP Server version 1.0.0 + ** Oracle Database MCP Toolkit version 1.0.0 ** ** Copyright (c) 2025 Oracle and/or its affiliates. ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -33,13 +33,31 @@ * to analyze and process Oracle JDBC and RDBMS/SQLNet log files. *

*/ -public final class OracleJDBCLogAnalyzer { +public final class LogAnalyzerTools { private static final String FILE_PATH = "filePath"; private static final String SECOND_FILE_PATH = "secondFilePath"; private static final String CONNECTION_ID = "connectionId"; - public static List getLogAnalyzerTools() { + /** + *

+ * Returns a list of available tools for Oracle JDBC Log Analyzer. + * The tools provided include: + *

    + *
  • {@code get-jdbc-stats}: Retrieves high-level statistics from an Oracle JDBC thin log file.
  • + *
  • {@code get-jdbc-queries}: Extracts all executed SQL queries from an Oracle JDBC thin log file.
  • + *
  • {@code get-jdbc-errors}: Processes a specified Oracle JDBC thin log file and extracts all reported errors.
  • + *
  • {@code list-log-files-from-directory}: Lists all Oracle JDBC log files present in the specified directory path.
  • + *
  • {@code jdbc-log-comparison}: Compares two Oracle JDBC log files and provides a JSON report highlighting differences and similarities.
  • + *
  • {@code get-jdbc-connection-events}: Retrieves opened and closed JDBC connection events from the log file.
  • + *
  • {@code get-rdbms-errors}: Processes a specified Oracle RDBMS/SQLNet trace file to extract all reported errors.
  • + *
  • {@code get-rdbms-packet-dumps}: Extracts packet dump information from a specified RDBMS/SQLNet trace file that matches a given connection ID.
  • + *
+ *

+ * + * @return a list of {@link McpServerFeatures.SyncToolSpecification SyncToolSpecification} instances representing the available tools. + */ + public static List getTools() { return List.of( getStatsTool(), getQueriesTool(), diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/SimilaritySearchTool.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/SimilaritySearchTool.java index 68049ccf..6f453aa3 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/SimilaritySearchTool.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/SimilaritySearchTool.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.tools; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -18,6 +25,12 @@ import static com.oracle.database.mcptoolkit.Utils.tryCall; import static com.oracle.database.mcptoolkit.Utils.getOrDefault; +/** + * Provides a tool for performing similarity searches using vector embeddings. + *

+ * This class is responsible for handling the "similarity_search" tool, which allows users to + * search for similar text based on a given query. + */ public class SimilaritySearchTool { private static final Pattern SAFE_IDENT = Pattern.compile("[A-Za-z0-9_$.#]+"); @@ -35,6 +48,14 @@ ORDER BY VECTOR_DISTANCE(%s, FETCH FIRST ? ROWS ONLY """; + /** + * Returns a tool specification for the "similarity_search" tool. + *

+ * This tool allows users to perform similarity searches using vector embeddings. + * + * @param config server configuration + * @return tool specification + */ public static McpServerFeatures.SyncToolSpecification getSymilaritySearchTool(ServerConfig config) { return McpServerFeatures.SyncToolSpecification.builder() diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ToolSchemas.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ToolSchemas.java index 9bb03ce6..0ef145dd 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ToolSchemas.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/tools/ToolSchemas.java @@ -1,7 +1,23 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.tools; +/** + * The ToolSchemas class provides a collection of JSON schemas for various tool-related operations. + * These schemas define the structure and constraints of the input data for different tools. + */ public class ToolSchemas { + /** + * JSON schema for SQL-only operations. + *

+ * This schema requires a "sql" property and optionally accepts a "txId" property. + */ static final String SQL_ONLY = """ { "type":"object", @@ -17,6 +33,11 @@ public class ToolSchemas { "required":["sql"] }"""; + /** + * JSON schema for file path operations. + *

+ * This schema requires a "filePath" property, which should be an absolute path or a URL to an Oracle JDBC log file. + */ static final String FILE_PATH_SCHEMA = """ { "type": "object", @@ -29,6 +50,12 @@ public class ToolSchemas { "required": ["filePath"] } """; + + /** + * JSON schema for file comparison operations. + *

+ * This schema requires "filePath" and "secondFilePath" properties, which should be absolute paths or URLs to Oracle JDBC log files. + */ static final String FILE_COMPARISON_SCHEMA = """ { "type": "object", @@ -45,6 +72,12 @@ public class ToolSchemas { "required": ["filePath", "secondFilePath"] } """; + + /** + * JSON schema for RDBMS tools operations. + *

+ * This schema requires "filePath" and "connectionId" properties, where "filePath" is an absolute path or a URL to an RDBMS/SQLNet trace file, and "connectionId" is a connection ID string. + */ static final String RDBMS_TOOLS_SCHEMA = """ { "type": "object", @@ -61,6 +94,12 @@ public class ToolSchemas { "required": ["filePath", "connectionId"] } """; + + /** + * JSON schema for similarity search operations. + *

+ * This schema requires a "question" property and optionally accepts several other properties to customize the search. + */ static final String SIMILARITY_SEARCH = """ { "type": "object", @@ -96,6 +135,12 @@ public class ToolSchemas { }, "required": ["question"] }"""; + + /** + * JSON schema for explain plan operations. + *

+ * This schema requires a "sql" property and optionally accepts several other properties to customize the plan. + */ static final String EXPLAIN_PLAN = """ { "type": "object", diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/AuthorizationFilter.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/AuthorizationFilter.java index 164caf7b..d94e046a 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/AuthorizationFilter.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/AuthorizationFilter.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.web; import com.oracle.database.mcptoolkit.oauth.OAuth2Configuration; @@ -12,9 +19,38 @@ import java.io.IOException; +/** + * The AuthorizationFilter class is a servlet filter that authenticates incoming requests + * by verifying the presence and validity of an OAuth2 access token in the Authorization header. + *

+ * If OAuth2 authentication is enabled (as determined by OAuth2Configuration), this filter + * checks the Authorization header for a Bearer token and validates it using an instance of + * OAuth2TokenValidator. If the token is invalid or missing, it returns a 401 Unauthorized response. + *

+ *

+ * The filter delegates to the next filter in the chain if the token is valid or if OAuth2 authentication + * is disabled. + *

+ */ public class AuthorizationFilter implements Filter { + /** + * Validator instance used to verify the validity of OAuth2 access tokens. + */ private static final OAuth2TokenValidator VALIDATOR = new OAuth2TokenValidator(); + /** + * Intercepts incoming requests to authenticate them based on the presence and validity of an OAuth2 access token. + *

+ * If OAuth2 authentication is enabled, it checks the Authorization header for a Bearer token and validates it. + * If the token is invalid or missing, it returns a 401 Unauthorized response. Otherwise, it delegates to the next filter in the chain. + *

+ * + * @param request the servlet request + * @param response the servlet response + * @param chain the filter chain + * @throws IOException if an I/O error occurs during the filtering process + * @throws ServletException if the filter chain fails + */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -39,6 +75,14 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha chain.doFilter(request, response); } + /** + * Handles authentication errors by returning a 401 Unauthorized response with a WWW-Authenticate header + * and a JSON payload containing error details. + * + * @param httpResponse the HTTP response + * @param httpRequest the HTTP request + * @throws IOException if an I/O error occurs while writing the response + */ private void handleError(HttpServletResponse httpResponse, HttpServletRequest httpRequest) throws IOException { final String serverURL = WebUtils.buildURLFromRequest(httpRequest); final var resourceMetadataURL = serverURL + "/.well-known/oauth-protected-resource"; diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/RedirectOAuthToOpenIDServlet.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/RedirectOAuthToOpenIDServlet.java index 355ab562..761192ad 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/RedirectOAuthToOpenIDServlet.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/RedirectOAuthToOpenIDServlet.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.web; import com.oracle.database.mcptoolkit.oauth.OAuth2Configuration; @@ -8,8 +15,26 @@ import java.io.IOException; +/** + * Servlet responsible for redirecting OAuth requests to the OpenID configuration endpoint. + *

+ * This servlet handles HTTP GET requests and redirects the client to the OpenID configuration endpoint + * specified in the OAuth2 configuration. + */ public class RedirectOAuthToOpenIDServlet extends HttpServlet { + /** + * Handles HTTP GET requests by redirecting the client to the OpenID configuration endpoint. + *

+ * The redirect URL is constructed using the authentication server URL from the OAuth2 configuration. + *

+ * The response includes an "Access-Control-Allow-Origin" header with the allowed hosts. + * + * @param request the HTTP request + * @param response the HTTP response + * @throws ServletException if a servlet-specific error occurs + * @throws IOException if an I/O error occurs + */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final String redirectLink = OAuth2Configuration.getInstance().getAuthServer() + diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WebUtils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WebUtils.java index 04749d1b..cf35f425 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WebUtils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WebUtils.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.web; import com.oracle.database.mcptoolkit.LoadedConstants; diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WellKnownServlet.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WellKnownServlet.java index d37d22e5..23385699 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WellKnownServlet.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/web/WellKnownServlet.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit.web; import com.oracle.database.mcptoolkit.oauth.OAuth2Configuration; @@ -8,9 +15,28 @@ import java.io.IOException; +/** + * The WellKnownServlet class handles HTTP GET requests to the well-known endpoint, + * providing information about the OAuth2 configuration and MCP endpoint. + * + */ public class WellKnownServlet extends HttpServlet { + /** + * The OAuth2 configuration instance. + */ private static final OAuth2Configuration OAUTH2_CONFIG = OAuth2Configuration.getInstance(); + /** + * Handles HTTP GET requests to the well-known endpoint. + * + * If OAuth2 is not configured or authentication is disabled, returns a 204 No Content response. + * Otherwise, returns a JSON response with the MCP endpoint and authorization server URL. + * + * @param request the HTTP request + * @param response the HTTP response + * @throws ServletException if a servlet-specific error occurs + * @throws IOException if an I/O error occurs + */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (!OAUTH2_CONFIG.isOAuth2Configured() || !OAUTH2_CONFIG.isAuthenticationEnabled()) { diff --git a/src/oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit/OracleJDBCLogAnalyzerTest.java b/src/oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit/OracleJDBCLogAnalyzerTest.java index 752d6948..1c2cc40b 100644 --- a/src/oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit/OracleJDBCLogAnalyzerTest.java +++ b/src/oracle-db-mcp-toolkit/src/test/java/com/oracle/database/mcptoolkit/OracleJDBCLogAnalyzerTest.java @@ -1,6 +1,6 @@ package com.oracle.database.mcptoolkit; -import com.oracle.database.mcptoolkit.tools.OracleJDBCLogAnalyzer; +import com.oracle.database.mcptoolkit.tools.LogAnalyzerTools; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.spec.McpSchema; import org.junit.jupiter.api.BeforeAll; @@ -21,7 +21,7 @@ class OracleJDBCLogAnalyzerTest { @BeforeAll static void initializeTools(){ - tools = OracleJDBCLogAnalyzer.getLogAnalyzerTools() + tools = LogAnalyzerTools.getTools() .stream() .map(SyncToolSpecification::tool) .collect(Collectors.toMap(McpSchema.Tool::name, identity())); From 333673fa5e4ddc11cd1efbe5669a78ebb4c303b8 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 10 Dec 2025 17:44:51 +0100 Subject: [PATCH 56/77] Rename mainClass to 'OracleDatabaseMCPToolkit' --- src/oracle-db-mcp-toolkit/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oracle-db-mcp-toolkit/pom.xml b/src/oracle-db-mcp-toolkit/pom.xml index d3707c86..2c1ccecf 100644 --- a/src/oracle-db-mcp-toolkit/pom.xml +++ b/src/oracle-db-mcp-toolkit/pom.xml @@ -103,7 +103,7 @@ false - com.oracle.database.mcptoolkit.OracleDBToolboxMCPServer + com.oracle.database.mcptoolkit.OracleDatabaseMCPToolkit From 22335262d36391246ad6e3f23191d4dca1cdd454 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Wed, 10 Dec 2025 17:51:52 +0100 Subject: [PATCH 57/77] rename oracle-db-toolbox-mcp-server to oracle-db-mcp-toolkit --- src/oracle-db-mcp-toolkit/README.md | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index 66f3dae9..f592dbdf 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -135,11 +135,11 @@ java -DconfigFile=/path/to/config.yaml -jar .jar mvn clean install ``` -The created jar can be found in `target/oracle-db-toolbox-mcp-server-1.0.0.jar`. +The created jar can be found in `target/oracle-db-mcp-toolkit-1.0.0.jar`. ### Transport modes (stdio vs HTTP) -`oracle-db-toolbox-mcp-server` supports two transport modes: +`oracle-db-mcp-toolkit` supports two transport modes: - **Stdio (default)** – the MCP client spawns the JVM process and talks over stdin/stdout - **HTTP (streamable)** – the MCP server runs as an HTTP service, and clients connect via a URL @@ -151,7 +151,7 @@ This is the mode used by tools like Claude Desktop, where the client directly la ```jsonc { "mcpServers": { - "oracle-db-toolbox-mcp-server": { + "oracle-db-mcp-toolkit": { "command": "java", "args": [ "-Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service", @@ -160,7 +160,7 @@ This is the mode used by tools like Claude Desktop, where the client directly la "-Dtools=get-jdbc-stats,get-jdbc-queries", "-Dojdbc.ext.dir=/path/to/extra-jars", "-jar", - "/oracle-db-toolbox-mcp-server-1.0.0.jar" + "/oracle-db-mcp-toolkit-1.0.0.jar" ] } } @@ -182,7 +182,7 @@ java \ -Ddb.user=your_user \ -Ddb.password=your_password \ -Dtools=get-jdbc-stats,get-jdbc-queries \ - -jar /oracle-db-toolbox-mcp-server-1.0.0.jar + -jar /oracle-db-mcp-toolkit-1.0.0.jar ``` This exposes the MCP endpoint at: `http://localhost:45450/mcp`. @@ -200,7 +200,7 @@ Cline supports streamable HTTP directly. Example: ```json { "mcpServers": { - "oracle-db-toolbox-mcp-server": { + "oracle-db-mcp-toolkit": { "type": "streamableHttp", "url": "http://localhost:45450/mcp" } @@ -216,7 +216,7 @@ you can use the `mcp-remote` workaround: ```json { "mcpServers": { - "oracle-db-toolbox-mcp-server": { + "oracle-db-mcp-toolkit": { "command": "npx", "args": [ "-y", @@ -267,7 +267,7 @@ java \ -DclientId=oracle-db-toolbox \ -DclientSecret=Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0 \ -DallowedHosts=http://localhost:6274 \ - -jar /oracle-db-toolbox-mcp-server-1.0.0.jar + -jar /oracle-db-mcp-toolkit-1.0.0.jar ``` In the above example, we configured OAuth2 with a local KeyCloak server with a realm named `mcp`, and we only allowed a local [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) @@ -284,7 +284,7 @@ java \ -Dtransport=http \ -Dhttp.port=45450 \ -DenableAuthentication=true \ - -jar /oracle-db-toolbox-mcp-server-1.0.0.jar + -jar /oracle-db-mcp-toolkit-1.0.0.jar ``` After starting the server, a UUID token will be generated and logged at INFO level: @@ -296,7 +296,7 @@ WARNING: OAuth2 is not configured Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.TokenGenerator INFO: Authorization token generated (for testing and development use only): 0dd11948-37a3-470f-911e-4cd8b3d6f69c Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.OracleDBToolboxMCPServer startHttpServer -INFO: [oracle-db-toolbox-mcp-server] HTTP transport started on http://localhost:45450 (endpoint: http://localhost:45450/mcp) +INFO: [oracle-db-mcp-toolkit] HTTP transport started on http://localhost:45450 (endpoint: http://localhost:45450/mcp) ``` If `ORACLE_DB_TOOLBOX_AUTH_TOKEN` environment variable is set: @@ -475,7 +475,7 @@ A `Dockerfile` is included at the root of the project so you can build and run t From the project root (where the Dockerfile lives): ```bash -podman build -t oracle-db-toolbox-mcp-server:1.0.0 . +podman build -t oracle-db-mcp-toolkit:1.0.0 . ``` ### Run the container (HTTP mode example) This example runs the MCP server over HTTP and HTTPS inside the container and exposes it on port 45450 and 45451 on your host. @@ -495,7 +495,7 @@ podman run --rm \ -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service \ -Ddb.user=your_user \ -Ddb.password=your_password" \ - oracle-db-toolbox-mcp-server:1.0.0 + oracle-db-mcp-toolkit:1.0.0 ``` This exposes the MCP endpoint at: http://[your-ip-address]:45450/mcp or https://[your-ip-address]:45451/mcp @@ -519,7 +519,7 @@ podman run --rm \ -Ddb.user=your_user \ -Ddb.password=your_password \ -Dojdbc.ext.dir=/ext" \ - oracle-db-toolbox-mcp-server:1.0.0 + oracle-db-mcp-toolkit:1.0.0 ``` ### Using Docker/Podman with stdio @@ -533,7 +533,7 @@ In this configuration, Claude Desktop runs `podman run --rm -i ... and connects ```json { "mcpServers": { - "oracle-db-toolbox-mcp-server": { + "oracle-db-mcp-toolkit": { "command": "podman", "args": [ "run", @@ -542,7 +542,7 @@ In this configuration, Claude Desktop runs `podman run --rm -i ... and connects "-v", "/absolute/path/to/ext:/ext:ro", "-e", "JAVA_TOOL_OPTIONS=-Dtools=get-jdbc-stats,get-jdbc-queries -Ddb.url=jdbc:oracle:thin:@your-host:1521/your-service -Ddb.user=your_user -Ddb.password=your_password -Dojdbc.ext.dir=/ext -DconfigFile=/config/config.yaml", - "oracle-db-toolbox-mcp-server:1.0.0" + "oracle-db-mcp-toolkit:1.0.0" ] } } From 26ec484be51bb957d75661830363b630c157f3b4 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Wed, 10 Dec 2025 18:12:35 +0100 Subject: [PATCH 58/77] Reorganising --- src/oracle-db-mcp-toolkit/README.md | 100 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index f592dbdf..b9153a3c 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -3,9 +3,58 @@ ## Overview Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that lets you: + * Define your own custom tools via a simple YAML configuration file. * Use 8 built-in tools to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. * Optionally use **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**, when JDBC configuration is provided. - * Define your own custom tools via a simple YAML configuration file. + +## Custom Tool Framework — Extending the MCP Server +The MCP server can load both database connection definitions and custom tool definitions from a YAML configuration file. +This provides a flexible and declarative way to extend the server without modifying or rebuilding the codebase. + +A YAML file may define: + +* One or more **sources:** — named database configurations (URL, user, password, etc.) + +* One or more **tools** — each with parameters, SQL statements, and optional metadata + +### Source Resolution Logic + +When executing a tool, the MCP server determines which source to use based on the following rules: + +1. If the tool specifies a source, that source is used. + +2. If the tool does not specify a source, the server looks for a default source: + + * First, it checks whether a source was provided via system properties (db.url, db.user, db.password) (Higher priority). + + * If no system property source is available, it falls back to the first source defined in the YAML file, if present. + +3. If no source can be resolved and the tool requires one (e.g., SQL-based tools), the server reports a configuration error. + +This design ensures that tools always have a predictable source while giving you flexibility to choose how connections are provided—either inline in YAML or externally via system properties and environment variables. + +**Example `config.yaml`:** +```yaml +sources: + prod-db: + url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 + user: ADMIN + password: ${password} + +tools: + hotels-by-name: + source: prod-db + parameters: + - name: name + type: string + description: Hotel name to search for. + required: false + statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' +``` +To enable YAML configuration, launch the server with: +```bash +java -DconfigFile=/path/to/config.yaml -jar .jar +``` ## Built-in Tools @@ -72,55 +121,6 @@ These tools operate on RDBMS/SQLNet trace files: --- -## Custom Tool Framework — Extending the MCP Server -The MCP server can load both database connection definitions and custom tool definitions from a YAML configuration file. -This provides a flexible and declarative way to extend the server without modifying or rebuilding the codebase. - -A YAML file may define: - - * One or more **sources:** — named database configurations (URL, user, password, etc.) - - * One or more **tools** — each with parameters, SQL statements, and optional metadata - -### Source Resolution Logic - -When executing a tool, the MCP server determines which source to use based on the following rules: - -1. If the tool specifies a source, that source is used. - -2. If the tool does not specify a source, the server looks for a default source: - - * First, it checks whether a source was provided via system properties (db.url, db.user, db.password) (Higher priority). - - * If no system property source is available, it falls back to the first source defined in the YAML file, if present. - -3. If no source can be resolved and the tool requires one (e.g., SQL-based tools), the server reports a configuration error. - -This design ensures that tools always have a predictable source while giving you flexibility to choose how connections are provided—either inline in YAML or externally via system properties and environment variables. - -**Example `config.yaml`:** -```yaml -sources: - prod-db: - url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 - user: ADMIN - password: ${password} - -tools: - hotels-by-name: - source: prod-db - parameters: - - name: name - type: string - description: Hotel name to search for. - required: false - statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' -``` -To enable YAML configuration, launch the server with: -```bash -java -DconfigFile=/path/to/config.yaml -jar .jar -``` - ## Prerequisites - **Java 17+** (JDK) From b627b587140cbb3ba723549bd7b7ef2de11f2160 Mon Sep 17 00:00:00 2001 From: Jean de Lavarene Date: Wed, 10 Dec 2025 18:32:08 +0100 Subject: [PATCH 59/77] Format changes --- src/oracle-db-mcp-toolkit/README.md | 59 +++++++++++++++++------------ 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index b9153a3c..70ad4546 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -1,13 +1,15 @@ # Oracle Database MCP Toolkit -## Overview +## 1. Overview Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that lets you: * Define your own custom tools via a simple YAML configuration file. * Use 8 built-in tools to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. * Optionally use **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**, when JDBC configuration is provided. -## Custom Tool Framework — Extending the MCP Server +--- + +## 2. Custom Tool Framework — Extending the MCP Server The MCP server can load both database connection definitions and custom tool definitions from a YAML configuration file. This provides a flexible and declarative way to extend the server without modifying or rebuilding the codebase. @@ -56,9 +58,11 @@ To enable YAML configuration, launch the server with: java -DconfigFile=/path/to/config.yaml -jar .jar ``` -## Built-in Tools +--- + +## 3. Built-in Tools -### Oracle JDBC Log Analysis: +### 3.1. Oracle JDBC Log Analysis: These tools operate on Oracle JDBC thin client logs: @@ -69,14 +73,14 @@ These tools operate on Oracle JDBC thin client logs: - **`list-log-files-from-directory`**: List all visible files from a specified directory, which helps the user analyze multiple files with one prompt. - **`jdbc-log-comparison`**: Compares two log files for performance metrics, errors, and network information. -### RDBMS/SQLNet Trace Analysis: +### 3.2. RDBMS/SQLNet Trace Analysis: These tools operate on RDBMS/SQLNet trace files: - **`get-rdbms-errors`**: Extracts errors from RDBMS/SQLNet trace files. - **`get-rdbms-packet-dumps`**: Extracts packet dumps for a specific connection ID. -### Vector Similarity Search +### 3.3. Vector Similarity Search * **`similarity_search`**: Perform semantic similarity search using Oracle’s vector features (`VECTOR_EMBEDDING`, `VECTOR_DISTANCE`). @@ -94,7 +98,7 @@ These tools operate on RDBMS/SQLNet trace files: * JSON array of similar rows with scores and truncated snippets. -### SQL Execution Plan Analysis +### 3.4. SQL Execution Plan Analysis * **`explain_plan`**: Generate Oracle execution plans and receive a pre-formatted LLM prompt for tuning and explanation. @@ -121,15 +125,16 @@ These tools operate on RDBMS/SQLNet trace files: --- -## Prerequisites +## 4. Installation +### 4.1. Prerequisites - **Java 17+** (JDK) - **Credentials** with permissions for your intended operations - **MCP client** (e.g., Claude Desktop) to call the tools > The server uses UCP pooling out of the box (initial/min= 1). -### Build the MCP server jar +### 4.2. Build the MCP server jar ```bash mvn clean install @@ -137,14 +142,14 @@ mvn clean install The created jar can be found in `target/oracle-db-mcp-toolkit-1.0.0.jar`. -### Transport modes (stdio vs HTTP) +### 4.3. Choose a transport mode (stdio vs HTTP) `oracle-db-mcp-toolkit` supports two transport modes: - **Stdio (default)** – the MCP client spawns the JVM process and talks over stdin/stdout - **HTTP (streamable)** – the MCP server runs as an HTTP service, and clients connect via a URL -#### Stdio mode (default) +#### 4.3.1. Stdio mode (default) This is the mode used by tools like Claude Desktop, where the client directly launches: @@ -168,7 +173,7 @@ This is the mode used by tools like Claude Desktop, where the client directly la ``` If you don’t set `-Dtransport`, the server runs in stdio mode by default. -#### HTTP mode +#### 4.3.2. HTTP mode In HTTP mode, you run the server as a standalone HTTP service and point an MCP client to it. @@ -186,7 +191,7 @@ java \ ``` This exposes the MCP endpoint at: `http://localhost:45450/mcp`. -#### Enabling HTTPS (SSL/TLS) +### 4.4. Enabling HTTPS (SSL/TLS) To enable HTTPS (SSL/TLS), specify your certificate keystore path and password using the `-DcertificatePath` and `-DcertificatePassword` options. Only PKCS12 (`.p12` or `.pfx`) keystore files are supported. You can set the HTTPS port with the `-Dhttps.port` option. @@ -194,7 +199,7 @@ You can set the HTTPS port with the `-Dhttps.port` option. ```shell -DcertificatePath=/path/to/your-certificate.p12 -DcertificatePassword=yourPassword -Dhttps.port=443 ``` -### Using HTTP from Cline +### 4.5. Using HTTP transport and Cline Cline supports streamable HTTP directly. Example: ```json @@ -208,7 +213,7 @@ Cline supports streamable HTTP directly. Example: } ``` -### Using HTTP from Claude Desktop +### 4.6. Using HTTP from Claude Desktop Claude Desktop accepts HTTPS endpoints for remote MCP servers. If your MCP server is only available over plain HTTP (e.g. http://localhost:45450/mcp), you can use the `mcp-remote` workaround: @@ -228,9 +233,9 @@ you can use the `mcp-remote` workaround: } ``` -### HTTP Authentication Configuration +### 4.7. HTTP Authentication Configuration -#### Generated Token (For Development and Testing) +#### 4.7.1. Generated Token (For Development and Testing) To enable authentication for the HTTP server, you must set the `-DenableAuthentication` system property to `true` (default value is `false`). If it's enabled (e.g. set to `true`) the MCP Server will check if there's an environment variable called `ORACLE_DB_TOOLBOX_AUTH_TOKEN` and its value will be used as a token. @@ -238,7 +243,7 @@ If the environment variable is not found, then a random UUID token will be gener When connecting to the MCP server, the token needs to be provided in the Authorization header of each request using the `Bearer ` prefix. -#### OAuth2 Configuration +#### 4.7.2. OAuth2 Configuration In order to configure an OAuth2 server, the `-DenableAuthentication` should be enabled alongside the following system properties: @@ -252,9 +257,9 @@ It works by creating an `/.well-known/oauth-authorization-server` endpoint on th For more details regarding this MCP and OAuth, please see [MCP specification for authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) (or a newer version if available). -#### Examples +##### Examples -##### Enabling Authentication with OAuth2 +###### Enabling Authentication with OAuth2 ```bash java \ @@ -318,7 +323,9 @@ INFO: Authorization token generated (for testing and development use only): Secr Ultimately, the token must be included in the http request header (e.g. `Authorization: Bearer 0dd11948-37a3-470f-911e-4cd8b3d6f69c` or `Authorization: Bearer Secret_DeV_T0ken`). -### Supported System Properties +--- + +## 5. Supported System Properties

45450
https.portNo + TCP port used for SSL connection. + 45451
certificatePathNo + Path to SSL certificate keystore (Support PKCS12) + /path/to/your/certificate
certificatePasswordNo + Password of SSL certificate keystore +
configFile No
@@ -467,17 +474,19 @@ If you enable **only** the Log Analyzer tools, you can omit db.url. * Note: If you’re using token-based authentication (e.g., IAM tokens) or a centralized configuration provided via the JARs you place in `-Dojdbc.ext.dir`, you can omit `db.user` and `db.password`. The driver will pick up credentials and security settings from those extensions. -## Docker Image +--- + +## 6. Docker Image A `Dockerfile` is included at the root of the project so you can build and run the MCP server as a container. -### Build the image +### 6.1. Build the image From the project root (where the Dockerfile lives): ```bash podman build -t oracle-db-mcp-toolkit:1.0.0 . ``` -### Run the container (HTTP mode example) +### 6.2. Run the container (HTTP mode example) This example runs the MCP server over HTTP and HTTPS inside the container and exposes it on port 45450 and 45451 on your host. ```bash @@ -522,7 +531,7 @@ podman run --rm \ oracle-db-mcp-toolkit:1.0.0 ``` -### Using Docker/Podman with stdio +### 6.3. Using Docker/Podman with stdio Instead of running the MCP server over HTTP, you can keep using the **stdio** transport and let your MCP client spawn the container (via **podman run**) instead of spawning java directly. In this mode, the MCP client talks to the server over stdin/stdout, just like with a local JAR. From 735292698268415328cbe2217aca8daef1c0a980 Mon Sep 17 00:00:00 2001 From: Jean de Lavarene Date: Wed, 10 Dec 2025 18:35:33 +0100 Subject: [PATCH 60/77] Add license --- .../database/mcptoolkit/OracleDatabaseMCPToolkit.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java index fedb3cf2..dbd6c95b 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit; import com.fasterxml.jackson.databind.ObjectMapper; From 6a0c87ffaa7dfc68a18e9cd6ba55e2fbe82293dd Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Wed, 10 Dec 2025 18:46:52 +0100 Subject: [PATCH 61/77] Adding license.txt and changing "source" to "dataSource" --- src/oracle-db-mcp-toolkit/LICENSE.txt | 35 +++++++++++++++++++ .../database/mcptoolkit/ServerConfig.java | 10 +++--- .../com/oracle/database/mcptoolkit/Utils.java | 8 ++--- .../mcptoolkit/config/ConfigRoot.java | 6 ++-- ...ourceConfig.java => DataSourceConfig.java} | 2 +- .../mcptoolkit/config/ToolConfig.java | 6 ++-- 6 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 src/oracle-db-mcp-toolkit/LICENSE.txt rename src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/{SourceConfig.java => DataSourceConfig.java} (98%) diff --git a/src/oracle-db-mcp-toolkit/LICENSE.txt b/src/oracle-db-mcp-toolkit/LICENSE.txt new file mode 100644 index 00000000..8dc7c070 --- /dev/null +++ b/src/oracle-db-mcp-toolkit/LICENSE.txt @@ -0,0 +1,35 @@ +Copyright (c) 2025 Oracle and/or its affiliates. + +The Universal Permissive License (UPL), Version 1.0 + +Subject to the condition set forth below, permission is hereby granted to any +person obtaining a copy of this software, associated documentation and/or data +(collectively the "Software"), free of charge and under any and all copyright +rights in the Software, and any and all patent rights owned or freely +licensable by each licensor hereunder covering either (i) the unmodified +Software as contributed to or provided by such licensor, or (ii) the Larger +Works (as defined below), to deal in both + +(a) the Software, and +(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +one is included with the Software (each a "Larger Work" to which the Software +is contributed by such licensors), + +without restriction, including without limitation the rights to copy, create +derivative works of, display, perform, and distribute the Software and make, +use, sell, offer for sale, import, export, have made, and have sold the +Software and the Larger Work(s), and to sublicense the foregoing rights on +either these or other terms. + +This license is subject to the following condition: +The above copyright notice and either this complete permission notice or at +a minimum a reference to the UPL must be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java index 16292a21..2a3425f6 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java @@ -8,7 +8,7 @@ package com.oracle.database.mcptoolkit; import com.oracle.database.mcptoolkit.config.ConfigRoot; -import com.oracle.database.mcptoolkit.config.SourceConfig; +import com.oracle.database.mcptoolkit.config.DataSourceConfig; import com.oracle.database.mcptoolkit.config.ToolConfig; import java.util.Collections; @@ -40,7 +40,7 @@ public final class ServerConfig { public final String dbUser; public final String dbPassword; public final Set toolsFilter; - public final Map sources; + public final Map sources; public final Map tools; public static String defaultSourceName; // Only if the default db info are from yaml config to avoid redundancy @@ -49,7 +49,7 @@ private ServerConfig( String dbUser, String dbPassword, Set toolsFilter, - Map sources, + Map sources, Map tools ) { this.dbUrl = dbUrl; @@ -90,7 +90,7 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St String dbUser = LoadedConstants.DB_USER; String dbPass = LoadedConstants.DB_PASSWORD; - Map sources = configRoot != null ? configRoot.sources : Collections.emptyMap(); + Map sources = configRoot != null ? configRoot.dataSources : Collections.emptyMap(); Map toolsMap = configRoot != null ? configRoot.tools : Collections.emptyMap(); if (toolsMap != null) { @@ -105,7 +105,7 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St && dbPass != null && !dbPass.isBlank(); if (!allLoadedConstantsPresent && sources!=null && sources.containsKey(defaultSourceKey)) { - SourceConfig src = sources.get(defaultSourceKey); + DataSourceConfig src = sources.get(defaultSourceKey); dbUrl = src.toJdbcUrl(); dbUser = src.user; dbPass = src.password; diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index 6c05e3c0..b5b79f87 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.oracle.database.mcptoolkit.config.ConfigRoot; -import com.oracle.database.mcptoolkit.config.SourceConfig; +import com.oracle.database.mcptoolkit.config.DataSourceConfig; import com.oracle.database.mcptoolkit.config.ToolConfig; import com.oracle.database.mcptoolkit.config.ToolParameterConfig; import com.oracle.database.mcptoolkit.tools.ExplainAndExecutePlanTool; @@ -102,7 +102,7 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) ) .callHandler((exchange, callReq) -> tryCall(() -> { - try (Connection c = openConnection(config, tc.source)) { + try (Connection c = openConnection(config, tc.dataSource)) { PreparedStatement ps = c.prepareStatement(tc.statement); int paramIdx = 1; if (tc.parameters != null) { @@ -163,7 +163,7 @@ static ServerConfig loadConfig() { if (yamlConfig == null) { config = ServerConfig.fromSystemProperties(); } else { - String defaultSourceKey = yamlConfig.sources!=null?yamlConfig.sources.keySet().stream().findFirst().orElse(null):null; + String defaultSourceKey = yamlConfig.dataSources!=null?yamlConfig.dataSources.keySet().stream().findFirst().orElse(null):null; config = ServerConfig.fromSystemPropertiesAndYaml(yamlConfig, defaultSourceKey); } return config; @@ -201,7 +201,7 @@ private static DataSource getOrCreateDataSource(ServerConfig cfg, String sourceN } else { return dataSources.computeIfAbsent(sourceName, name -> { try { - SourceConfig src = (cfg.sources != null) ? cfg.sources.get(name) : null; + DataSourceConfig src = (cfg.sources != null) ? cfg.sources.get(name) : null; if (src == null) throw new IllegalArgumentException("Unknown source: " + name); return createDataSource(src.toJdbcUrl(), src.user, src.password); } catch (SQLException ex) { diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java index 27927e7a..38fcca68 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java @@ -13,7 +13,7 @@ * Represents the root configuration for the application, containing a map of source configurations and tool configurations. */ public class ConfigRoot { - public Map sources; + public Map dataSources; public Map tools; /** @@ -23,8 +23,8 @@ public class ConfigRoot { * in each configuration's fields. */ public void substituteEnvVars() { - if (sources != null) { - for (SourceConfig sc : sources.values()) { + if (dataSources != null) { + for (DataSourceConfig sc : dataSources.values()) { if (sc != null) sc.substituteEnvVars(); } } diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java similarity index 98% rename from src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java rename to src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java index 624f3f63..1799f2c6 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/SourceConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java @@ -14,7 +14,7 @@ *

* This class encapsulates the necessary properties to establish a connection to the database. */ -public class SourceConfig { +public class DataSourceConfig { /** * The hostname or IP address of the database server. */ diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java index 8c6acdd0..2e949e9f 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java @@ -24,9 +24,9 @@ public class ToolConfig { public String name; /** - * Reference key from sources. + * Reference key from data sources. */ - public String source; + public String dataSource; /** * A brief description of the tool. @@ -51,7 +51,7 @@ public class ToolConfig { */ public void substituteEnvVars() { this.name = EnvSubstitutor.substituteEnvVars(this.name); - this.source = EnvSubstitutor.substituteEnvVars(this.source); + this.dataSource = EnvSubstitutor.substituteEnvVars(this.dataSource); this.description = EnvSubstitutor.substituteEnvVars(this.description); this.statement = EnvSubstitutor.substituteEnvVars(this.statement); if (this.parameters != null) { From befbd2cc7cac1f355b89714d98d0d5e91bd8e9e8 Mon Sep 17 00:00:00 2001 From: Jean de Lavarene Date: Wed, 10 Dec 2025 18:58:23 +0100 Subject: [PATCH 62/77] Renaming toolbox to toolkit --- src/oracle-db-mcp-toolkit/Dockerfile | 6 +++--- src/oracle-db-mcp-toolkit/pom.xml | 9 ++++++--- .../database/mcptoolkit/OracleDatabaseMCPToolkit.java | 6 +++--- .../main/java/com/oracle/database/mcptoolkit/Utils.java | 8 ++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/Dockerfile b/src/oracle-db-mcp-toolkit/Dockerfile index 5e0285fc..2034a156 100644 --- a/src/oracle-db-mcp-toolkit/Dockerfile +++ b/src/oracle-db-mcp-toolkit/Dockerfile @@ -29,7 +29,7 @@ RUN useradd -r -u 10001 appuser && \ WORKDIR /app USER appuser -COPY --from=builder /src/target/oracle-db-toolbox-mcp-server-*.jar \ - /app/oracle-db-toolbox-mcp-server.jar +COPY --from=builder /src/target/oracle-db-mcp-toolkit-*.jar \ + /app/oracle-db-mcp-toolkit.jar -ENTRYPOINT ["java", "-jar", "/app/oracle-db-toolbox-mcp-server.jar"] \ No newline at end of file +ENTRYPOINT ["java", "-jar", "/app/oracle-db-mcp-toolkit.jar"] \ No newline at end of file diff --git a/src/oracle-db-mcp-toolkit/pom.xml b/src/oracle-db-mcp-toolkit/pom.xml index 2c1ccecf..9191e511 100644 --- a/src/oracle-db-mcp-toolkit/pom.xml +++ b/src/oracle-db-mcp-toolkit/pom.xml @@ -8,9 +8,12 @@ oracle-db-mcp-toolkit 1.0.0 jar - Oracle JDBC Log Analyzer MCP Server - The Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that enables users to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files using built-in tools. - It also offers database-powered tools, such as vector similarity search and SQL execution plan analysis. Additionally, users can define custom tools through a YAML configuration file. + Oracle Database MCP Toolkit + The Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that + enables users to define custom tools through a YAML configuration file, analyze Oracle + JDBC thin client logs and RDBMS/SQLNet trace files using built-in tools. + It also offers database-powered tools, such as vector similarity search and SQL execution + plan analysis. https://github.com/oracle/mcp/tree/main/src/oracle-db-mcp-toolkit diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java index dbd6c95b..72ced300 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java @@ -57,7 +57,7 @@ public static void main(String[] args) { case "stdio" -> { server = McpServer .sync(new StdioServerTransportProvider(new ObjectMapper())) - .serverInfo("oracle-db-toolbox-mcp-server", "1.0.0") + .serverInfo("oracle-db-mcp-toolkit", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder() .tools(true) .logging() @@ -88,7 +88,7 @@ private static McpSyncServer startHttpServer() { McpSyncServer server = McpServer .sync(transport) - .serverInfo("oracle-db-toolbox-mcp-server", "1.0.0") + .serverInfo("oracle-db-mcp-toolkit", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder() .tools(true) .logging() @@ -140,7 +140,7 @@ private static McpSyncServer startHttpServer() { tomcat.start(); - LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + LoadedConstants.HTTP_PORT + " (endpoint: /mcp)"); + LOG.info(() -> "[oracle-db-mcp-toolkit] HTTP transport started on " + LoadedConstants.HTTP_PORT + " (endpoint: /mcp)"); return server; } catch (Exception e) { diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index 6c05e3c0..1d8c8338 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -282,7 +282,7 @@ static void installExternalExtensionsFromDir() { final Path root = Paths.get(dir); if (!Files.isDirectory(root)) { - LOG.warning("[oracle-db-toolbox-mcp-server] ojdbc.ext.dir is not a directory: " + dir); + LOG.warning("[oracle-db-mcp-toolkit] ojdbc.ext.dir is not a directory: " + dir); return; } final List jarUrls = new ArrayList<>(); @@ -292,16 +292,16 @@ static void installExternalExtensionsFromDir() { try { jarUrls.add(p.toUri().toURL()); } catch (Exception e) { - LOG.log(Level.WARNING, "[oracle-db-toolbox-mcp-server] Failed to add jar: " + p, e); + LOG.log(Level.WARNING, "[oracle-db-mcp-toolkit] Failed to add jar: " + p, e); } }); } catch (Exception e) { - LOG.log(Level.WARNING, "[oracle-db-toolbox-mcp-server] Failed to scan " + dir, e); + LOG.log(Level.WARNING, "[oracle-db-mcp-toolkit] Failed to scan " + dir, e); return; } if (jarUrls.isEmpty()) { - LOG.warning("[oracle-db-toolbox-mcp-server] No jars found under " + dir); + LOG.warning("[oracle-db-mcp-toolkit] No jars found under " + dir); return; } From 4ff52aed1ffc28377f36679c3f3541f5ad6d5fdc Mon Sep 17 00:00:00 2001 From: Jean de Lavarene Date: Wed, 10 Dec 2025 19:03:25 +0100 Subject: [PATCH 63/77] Renaming toolbox to toolkit --- src/oracle-db-mcp-toolkit/README.md | 14 +++++++------- .../database/mcptoolkit/LoadedConstants.java | 2 +- .../database/mcptoolkit/oauth/TokenGenerator.java | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index 70ad4546..028ee398 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -238,7 +238,7 @@ you can use the `mcp-remote` workaround: #### 4.7.1. Generated Token (For Development and Testing) To enable authentication for the HTTP server, you must set the `-DenableAuthentication` system property to `true` (default value is `false`). -If it's enabled (e.g. set to `true`) the MCP Server will check if there's an environment variable called `ORACLE_DB_TOOLBOX_AUTH_TOKEN` and its value will be used as a token. +If it's enabled (e.g. set to `true`) the MCP Server will check if there's an environment variable called `ORACLE_DB_TOOLKIT_AUTH_TOKEN` and its value will be used as a token. If the environment variable is not found, then a random UUID token will be generated once per JVM session. The token would be logged at the `INFO` level. When connecting to the MCP server, the token needs to be provided in the Authorization header of each request using the `Bearer ` prefix. @@ -251,7 +251,7 @@ In order to configure an OAuth2 server, the `-DenableAuthentication` should be e - `-DredirectOAuthToOpenID`: (default: `false`) This system property is used to as a workaround to support OAuth servers that provide `/.well-known/openid-configuration` and not `/.well-known/oauth-authorization-server`. It works by creating an `/.well-known/oauth-authorization-server` endpoint on the MCP Server that redirects to the OAuth server's `/.well-known/openid-configuration` endpoint. - `-DintrospectionEndpoint`: The OAuth2 server's introspection endpoint used to validate an access token (The OAuth2 introspection JSON response MUST contain the `active` field, e.g. `{...,"active": false,..}`). -- `-DclientId`: Client ID (e.g. `oracle-db-toolbox`) +- `-DclientId`: Client ID (e.g. `oracle-db-toolkit`) - `-DclientSecret`: Client Secret (e.g. `Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0`) - `-DallowedHosts`: (default: `*`) The value of `Access-Control-Allow-Origin` header when requesting the `/.well-known/oauth-protected-resource` endpoint (and `/.well-known/oauth-authorization-server` if `-DredirectOAuthToOpenID` is set to `true`) of the MCP Server. @@ -269,7 +269,7 @@ java \ -DenableAuthentication=true \ -DauthServer=http://localhost:8080/realms/mcp \ -DintrospectionEndpoint=http://localhost:8080/realms/mcp/protocol/openid-connect/token/introspect \ - -DclientId=oracle-db-toolbox \ + -DclientId=oracle-db-toolkit \ -DclientSecret=Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0 \ -DallowedHosts=http://localhost:6274 \ -jar /oracle-db-mcp-toolkit-1.0.0.jar @@ -300,14 +300,14 @@ Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.OAuth2Configuration INFO: Authorization token generated (for testing and development use only): 0dd11948-37a3-470f-911e-4cd8b3d6f69c -Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.OracleDBToolboxMCPServer startHttpServer +Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.OracleDatabaseMCPToolkit startHttpServer INFO: [oracle-db-mcp-toolkit] HTTP transport started on http://localhost:45450 (endpoint: http://localhost:45450/mcp) ``` -If `ORACLE_DB_TOOLBOX_AUTH_TOKEN` environment variable is set: +If `ORACLE_DB_TOOLKIT_AUTH_TOKEN` environment variable is set: ```bash -export ORACLE_DB_TOOLBOX_AUTH_TOKEN=Secret_DeV_T0ken +export ORACLE_DB_TOOLKIT_AUTH_TOKEN=Secret_DeV_T0ken ``` Then the server logs will be the following: @@ -442,7 +442,7 @@ Ultimately, the token must be included in the http request header (e.g. `Authori

- + diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java index 52b96cbd..5a94df36 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java @@ -36,7 +36,7 @@ private LoadedConstants() {} // Prevent instantiation public static final String ALLOWED_HOSTS= System.getProperty("allowedHosts","*"); public static final String REDIRECT_OPENID_TO_OAUTH= System.getProperty("redirectOpenIDToOAuth","false"); public static final boolean ENABLE_AUTH = Boolean.parseBoolean(System.getProperty("enableAuthentication","false")); - public static final String ORACLE_DB_TOOLBOX_AUTH_TOKEN = System.getenv("ORACLE_DB_TOOLBOX_AUTH_TOKEN"); + public static final String ORACLE_DB_TOOLKIT_AUTH_TOKEN = System.getenv("ORACLE_DB_TOOLKIT_AUTH_TOKEN"); public static final String AUTH_SERVER = System.getProperty("authServer"); public static final String INTROSPECTION_ENDPOINT = System.getProperty("introspectionEndpoint"); public static final String CLIENT_ID = System.getProperty("clientId"); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java index 0e4a02d4..a7a82d81 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java @@ -7,7 +7,7 @@ package com.oracle.database.mcptoolkit.oauth; -import static com.oracle.database.mcptoolkit.LoadedConstants.ORACLE_DB_TOOLBOX_AUTH_TOKEN; +import static com.oracle.database.mcptoolkit.LoadedConstants.ORACLE_DB_TOOLKIT_AUTH_TOKEN; import java.util.UUID; import java.util.logging.Level; @@ -31,7 +31,7 @@ public class TokenGenerator { * Initializes the generated token based on the {@code ORACLE_DB_TOOLBOX_AUTH_TOKEN} environment variable or a random UUID. */ private TokenGenerator() { - generatedToken = ORACLE_DB_TOOLBOX_AUTH_TOKEN != null ? ORACLE_DB_TOOLBOX_AUTH_TOKEN : UUID.randomUUID().toString() ; + generatedToken = ORACLE_DB_TOOLKIT_AUTH_TOKEN != null ? ORACLE_DB_TOOLKIT_AUTH_TOKEN : UUID.randomUUID().toString() ; LOG.log(Level.INFO, "Authorization token generated (for testing and development use only): {0}", generatedToken); } From edf3c3077a5f7d175d386f52ae90446afba291bd Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 11 Dec 2025 01:25:46 +0100 Subject: [PATCH 64/77] Updating readme with datasource --- src/oracle-db-mcp-toolkit/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index b9153a3c..655f1c32 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -13,29 +13,29 @@ This provides a flexible and declarative way to extend the server without modify A YAML file may define: -* One or more **sources:** — named database configurations (URL, user, password, etc.) +* One or more **datasources:** — named database configurations (URL, user, password, etc.) * One or more **tools** — each with parameters, SQL statements, and optional metadata -### Source Resolution Logic +### DataSource Resolution Logic -When executing a tool, the MCP server determines which source to use based on the following rules: +When executing a tool, the MCP server determines which datasource to use based on the following rules: -1. If the tool specifies a source, that source is used. +1. If the tool specifies a datasource, that datasource is used. -2. If the tool does not specify a source, the server looks for a default source: +2. If the tool does not specify a datasource, the server looks for a default datasource: - * First, it checks whether a source was provided via system properties (db.url, db.user, db.password) (Higher priority). + * First, it checks whether a datasource was provided via system properties (db.url, db.user, db.password) (Higher priority). - * If no system property source is available, it falls back to the first source defined in the YAML file, if present. + * If no system property datasource is available, it falls back to the first datasource defined in the YAML file, if present. -3. If no source can be resolved and the tool requires one (e.g., SQL-based tools), the server reports a configuration error. +3. If no datasource can be resolved and the tool requires one (e.g., SQL-based tools), the server reports a configuration error. -This design ensures that tools always have a predictable source while giving you flexibility to choose how connections are provided—either inline in YAML or externally via system properties and environment variables. +This design ensures that tools always have a predictable datasource while giving you flexibility to choose how connections are provided—either inline in YAML or externally via system properties and environment variables. **Example `config.yaml`:** ```yaml -sources: +dataSources: prod-db: url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 user: ADMIN @@ -43,7 +43,7 @@ sources: tools: hotels-by-name: - source: prod-db + dataSource: prod-db parameters: - name: name type: string @@ -409,7 +409,7 @@ Ultimately, the token must be included in the http request header (e.g. `Authori - + From 38b46f5ef4326eae4bfb3155111f1eec2a633255 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 11 Dec 2025 10:07:36 +0100 Subject: [PATCH 65/77] Making fields private and using getters instead --- .../database/mcptoolkit/ServerConfig.java | 10 ++--- .../com/oracle/database/mcptoolkit/Utils.java | 22 +++++----- .../mcptoolkit/config/ConfigRoot.java | 12 ++++- .../mcptoolkit/config/DataSourceConfig.java | 20 ++++++--- .../mcptoolkit/config/ToolConfig.java | 44 ++++++++++++++----- .../config/ToolParameterConfig.java | 24 ++++++++-- 6 files changed, 94 insertions(+), 38 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java index 2a3425f6..3f27465b 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java @@ -90,12 +90,12 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St String dbUser = LoadedConstants.DB_USER; String dbPass = LoadedConstants.DB_PASSWORD; - Map sources = configRoot != null ? configRoot.dataSources : Collections.emptyMap(); - Map toolsMap = configRoot != null ? configRoot.tools : Collections.emptyMap(); + Map sources = configRoot != null ? configRoot.getDataSources() : Collections.emptyMap(); + Map toolsMap = configRoot != null ? configRoot.getTools() : Collections.emptyMap(); if (toolsMap != null) { for (Map.Entry entry : toolsMap.entrySet()) { - entry.getValue().name = entry.getKey(); + entry.getValue().setName(entry.getKey()); } } configRoot.substituteEnvVars(); @@ -107,8 +107,8 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St if (!allLoadedConstantsPresent && sources!=null && sources.containsKey(defaultSourceKey)) { DataSourceConfig src = sources.get(defaultSourceKey); dbUrl = src.toJdbcUrl(); - dbUser = src.user; - dbPass = src.password; + dbUser = src.getUser(); + dbPass = src.getPassword(); defaultSourceName = defaultSourceKey; } diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index b5b79f87..1ed3cb70 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -94,24 +94,24 @@ static void addSyncToolSpecifications(McpSyncServer server, ServerConfig config) server.addTool( McpServerFeatures.SyncToolSpecification.builder() .tool(McpSchema.Tool.builder() - .name(tc.name) - .title(tc.name) - .description(tc.description) + .name(tc.getName()) + .title(tc.getName()) + .description(tc.getDescription()) .inputSchema(tc.buildInputSchemaJson()) .build() ) .callHandler((exchange, callReq) -> tryCall(() -> { - try (Connection c = openConnection(config, tc.dataSource)) { - PreparedStatement ps = c.prepareStatement(tc.statement); + try (Connection c = openConnection(config, tc.getDataSource())) { + PreparedStatement ps = c.prepareStatement(tc.getStatement()); int paramIdx = 1; - if (tc.parameters != null) { - for (ToolParameterConfig param : tc.parameters) { - Object argVal = callReq.arguments().get(param.name); + if (tc.getParameters() != null) { + for (ToolParameterConfig param : tc.getParameters()) { + Object argVal = callReq.arguments().get(param.getName()); ps.setObject(paramIdx++, argVal); } } - if (tc.statement.trim().toLowerCase().startsWith("select")) { + if (tc.getStatement().trim().toLowerCase().startsWith("select")) { ResultSet rs = ps.executeQuery(); List> rows = rsToList(rs); return McpSchema.CallToolResult.builder() @@ -163,7 +163,7 @@ static ServerConfig loadConfig() { if (yamlConfig == null) { config = ServerConfig.fromSystemProperties(); } else { - String defaultSourceKey = yamlConfig.dataSources!=null?yamlConfig.dataSources.keySet().stream().findFirst().orElse(null):null; + String defaultSourceKey = yamlConfig.getDataSources()!=null?yamlConfig.getDataSources().keySet().stream().findFirst().orElse(null):null; config = ServerConfig.fromSystemPropertiesAndYaml(yamlConfig, defaultSourceKey); } return config; @@ -203,7 +203,7 @@ private static DataSource getOrCreateDataSource(ServerConfig cfg, String sourceN try { DataSourceConfig src = (cfg.sources != null) ? cfg.sources.get(name) : null; if (src == null) throw new IllegalArgumentException("Unknown source: " + name); - return createDataSource(src.toJdbcUrl(), src.user, src.password); + return createDataSource(src.toJdbcUrl(), src.getUser(), src.getPassword()); } catch (SQLException ex) { throw new RuntimeException(ex); } diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java index 38fcca68..80ab0d66 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ConfigRoot.java @@ -13,8 +13,16 @@ * Represents the root configuration for the application, containing a map of source configurations and tool configurations. */ public class ConfigRoot { - public Map dataSources; - public Map tools; + private Map dataSources; + private Map tools; + + public Map getTools() { + return tools; + } + + public Map getDataSources() { + return dataSources; + } /** * Substitutes environment variables in the source and tool configurations. diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java index 1799f2c6..e17b01e7 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java @@ -18,33 +18,33 @@ public class DataSourceConfig { /** * The hostname or IP address of the database server. */ - public String host; + private String host; /** * The port number on which the database server is listening. */ - public String port; + private String port; /** * The Oracle SID or service name of the database. */ - public String database; + private String database; /** * The JDBC URL for the database connection. If not provided, it will be constructed * using the host, port, and database properties. */ - public String url; + private String url; /** * The username to use for the database connection. */ - public String user; + private String user; /** * The password to use for the database connection. */ - public String password; + private String password; /** * Returns the JDBC URL for the database connection. If the {@link #url} property is not set, @@ -73,4 +73,12 @@ public void substituteEnvVars() { this.user = EnvSubstitutor.substituteEnvVars(this.user); this.password = EnvSubstitutor.substituteEnvVars(this.password); } + + public String getUser() { + return user; + } + + public String getPassword() { + return password; + } } \ No newline at end of file diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java index 2e949e9f..e27710ba 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolConfig.java @@ -21,27 +21,27 @@ public class ToolConfig { /** * The tool name, derived from the YAML key. */ - public String name; + private String name; /** * Reference key from data sources. */ - public String dataSource; + private String dataSource; /** * A brief description of the tool. */ - public String description; + private String description; /** * A list of parameter configurations for the tool. */ - public List parameters; + private List parameters; /** * The SQL statement to be executed by the tool. */ - public String statement; + private String statement; /** * Substitutes environment variables in the tool configuration. @@ -71,11 +71,11 @@ public String buildInputSchemaJson() { if (param == null) { continue; } - ObjectNode prop = properties.putObject(param.name); - prop.put("type", param.type); - prop.put("description", param.description); - if (param.required) { - required.add(param.name); + ObjectNode prop = properties.putObject(param.getName()); + prop.put("type", param.getType()); + prop.put("description", param.getDescription()); + if (param.isRequired()) { + required.add(param.getName()); } } if (!required.isEmpty()) { @@ -83,4 +83,28 @@ public String buildInputSchemaJson() { } return schema.toString(); } + + public String getName() { + return name; + } + + public String getDataSource() { + return dataSource; + } + + public String getDescription() { + return description; + } + + public List getParameters() { + return parameters; + } + + public String getStatement() { + return statement; + } + + public void setName(String name) { + this.name = name; + } } \ No newline at end of file diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java index 8573590d..59b2f851 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/ToolParameterConfig.java @@ -18,22 +18,22 @@ public class ToolParameterConfig { /** * The name of the tool parameter. */ - public String name; + private String name; /** * The data type of the tool parameter. */ - public String type; + private String type; /** * A human-readable description of the tool parameter. */ - public String description; + private String description; /** * Indicates whether the tool parameter is required. */ - public boolean required; + private boolean required; /** * Substitutes environment variables in the tool parameter's properties. @@ -45,4 +45,20 @@ public void substituteEnvVars() { this.type = EnvSubstitutor.substituteEnvVars(this.type); this.description = EnvSubstitutor.substituteEnvVars(this.description); } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String getDescription() { + return description; + } + + public boolean isRequired() { + return required; + } } \ No newline at end of file From 04bc415b41d45aa810e39b78d4afd49cf641b497 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 11 Dec 2025 10:20:41 +0100 Subject: [PATCH 66/77] Adding javadoc for enableHttps method --- .../database/mcptoolkit/OracleDatabaseMCPToolkit.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java index fedb3cf2..55cababf 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java @@ -141,6 +141,14 @@ private static McpSyncServer startHttpServer() { } } + /** + * Configures and enables HTTPS on the provided Tomcat server using the specified keystore. + * + * @param tomcat the Tomcat server instance to configure + * @param keystorePath the file path to the PKCS12 keystore containing the SSL certificate + * @param keystorePassword the password for the keystore + * @throws RuntimeException if the HTTPS connector or SSL configuration fails + */ private static void enableHttps(Tomcat tomcat, String keystorePath, String keystorePassword) { try { // Create HTTPS connector From 8365d65b6602029964f4c2e0b70673f7caf789b9 Mon Sep 17 00:00:00 2001 From: Mouhsin Elmajdouby Date: Thu, 11 Dec 2025 14:45:31 +0100 Subject: [PATCH 67/77] Clarify README wording for vault-based providers --- src/oracle-db-mcp-toolkit/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index 90a003a7..ff9b49ae 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -510,7 +510,8 @@ This exposes the MCP endpoint at: http://[your-ip-address]:45450/mcp or https:// You can then configure Cline or Claude Desktop as described in the Using HTTP from Cline / Claude Desktop sections above. -If you need extra JDBC / security jars (e.g. `oraclepki`, `wallets`, `centralized config`), +If you need extra JDBC / security jars (e.g. `oraclepki`, wallets, centralized config, or providers that fetch full +database credentials such as username, password, and connection string from a vault secret), mount them and point `ojdbc.ext.dir` at that directory: ```bash From 2de9d4d1a75baa1796dc7f5bf9b091ee74fec2ca Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 11 Dec 2025 10:20:41 +0100 Subject: [PATCH 68/77] Adding javadoc for enableHttps method --- .../database/mcptoolkit/OracleDatabaseMCPToolkit.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java index fedb3cf2..55cababf 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java @@ -141,6 +141,14 @@ private static McpSyncServer startHttpServer() { } } + /** + * Configures and enables HTTPS on the provided Tomcat server using the specified keystore. + * + * @param tomcat the Tomcat server instance to configure + * @param keystorePath the file path to the PKCS12 keystore containing the SSL certificate + * @param keystorePassword the password for the keystore + * @throws RuntimeException if the HTTPS connector or SSL configuration fails + */ private static void enableHttps(Tomcat tomcat, String keystorePath, String keystorePassword) { try { // Create HTTPS connector From 64e6ed092fb5b8e81e0068c57323966b3f761f8f Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 11 Dec 2025 14:46:50 +0100 Subject: [PATCH 69/77] Using char[] for passwords instead of String # Conflicts: # src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java --- .../database/mcptoolkit/EnvSubstitutor.java | 32 +++++++++++++++++++ .../database/mcptoolkit/LoadedConstants.java | 4 ++- .../database/mcptoolkit/ServerConfig.java | 10 +++--- .../com/oracle/database/mcptoolkit/Utils.java | 4 +-- .../mcptoolkit/config/DataSourceConfig.java | 4 +-- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java index 44635dc2..fd9944bf 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/EnvSubstitutor.java @@ -7,6 +7,7 @@ package com.oracle.database.mcptoolkit; +import java.util.Arrays; import java.util.regex.*; /** @@ -42,4 +43,35 @@ public static String substituteEnvVars(String input) { m.appendTail(sb); return sb.toString(); } + + public static char[] substituteEnvVarsInCharArray(char[] input) { + if (input == null) return null; + StringBuilder output = new StringBuilder(input.length * 2); // Maybe bigger due to expansions + for (int i = 0; i < input.length; ) { + if (input[i] == '$' && (i + 1) < input.length && input[i + 1] == '{') { + int end = i + 2; + while (end < input.length && input[end] != '}') { + end++; + } + if (end < input.length) { + // Extract variable name + String varName = new String(input, i + 2, end - (i + 2)); + String value = System.getenv(varName); + if (value == null) { + throw new IllegalStateException("Missing environment variable for: " + varName); + } + output.append(value); + i = end + 1; + continue; + } + } + output.append(input[i]); + i++; + } + char[] result = new char[output.length()]; + output.getChars(0, output.length(), result, 0); + // Clear input + Arrays.fill(input, '\0'); + return result; + } } \ No newline at end of file diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java index 52b96cbd..79ab238e 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java @@ -30,7 +30,9 @@ private LoadedConstants() {} // Prevent instantiation public static final String TOOLS = System.getProperty("tools"); public static final String DB_URL = System.getProperty("db.url"); public static final String DB_USER = System.getProperty("db.user"); - public static final String DB_PASSWORD = System.getProperty("db.password"); + public static final char[] DB_PASSWORD = System.getProperty("db.password") != null + ? System.getProperty("db.password").toCharArray() + : null; /** OAuth config */ public static final String ALLOWED_HOSTS= System.getProperty("allowedHosts","*"); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java index 2a3425f6..c432dc4b 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java @@ -38,7 +38,7 @@ public final class ServerConfig { public final String dbUrl; public final String dbUser; - public final String dbPassword; + public final char[] dbPassword; public final Set toolsFilter; public final Map sources; public final Map tools; @@ -47,7 +47,7 @@ public final class ServerConfig { private ServerConfig( String dbUrl, String dbUser, - String dbPassword, + char[] dbPassword, Set toolsFilter, Map sources, Map tools @@ -88,7 +88,7 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St String dbUrl = LoadedConstants.DB_URL; String dbUser = LoadedConstants.DB_USER; - String dbPass = LoadedConstants.DB_PASSWORD; + char[] dbPass = LoadedConstants.DB_PASSWORD; Map sources = configRoot != null ? configRoot.dataSources : Collections.emptyMap(); Map toolsMap = configRoot != null ? configRoot.tools : Collections.emptyMap(); @@ -102,7 +102,7 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St boolean allLoadedConstantsPresent = dbUrl != null && !dbUrl.isBlank() && dbUser != null && !dbUser.isBlank() - && dbPass != null && !dbPass.isBlank(); + && dbPass != null && dbPass.length > 0; if (!allLoadedConstantsPresent && sources!=null && sources.containsKey(defaultSourceKey)) { DataSourceConfig src = sources.get(defaultSourceKey); @@ -118,7 +118,7 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St if (needDb && (dbUser == null || dbUser.isBlank())) { throw new IllegalStateException("Missing required db.user in both system properties and YAML config"); } - if (needDb && (dbPass == null || dbPass.isBlank())) { + if (needDb && (dbPass == null || dbPass.length == 0)) { throw new IllegalStateException("Missing required db.password in both system properties and YAML config"); } return new ServerConfig(dbUrl, dbUser, dbPass, tools, sources, toolsMap); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index b5b79f87..0901a568 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -211,12 +211,12 @@ private static DataSource getOrCreateDataSource(ServerConfig cfg, String sourceN } } - private static DataSource createDataSource(String url, String user, String password) throws SQLException { + private static DataSource createDataSource(String url, String user, char[] password) throws SQLException { PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); pds.setURL(url); if (user != null) pds.setUser(user); - if (password != null) pds.setPassword(password); + if (password != null) pds.setPassword(new String(password)); pds.setInitialPoolSize(1); pds.setMinPoolSize(1); pds.setConnectionWaitTimeout(10); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java index 1799f2c6..9b0b8c51 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java @@ -44,7 +44,7 @@ public class DataSourceConfig { /** * The password to use for the database connection. */ - public String password; + public char[] password; /** * Returns the JDBC URL for the database connection. If the {@link #url} property is not set, @@ -71,6 +71,6 @@ public void substituteEnvVars() { this.database = EnvSubstitutor.substituteEnvVars(this.database); this.url = EnvSubstitutor.substituteEnvVars(this.url); this.user = EnvSubstitutor.substituteEnvVars(this.user); - this.password = EnvSubstitutor.substituteEnvVars(this.password); + this.password = EnvSubstitutor.substituteEnvVarsInCharArray(this.password); } } \ No newline at end of file From 53a5acaa84bee22089c532acc87af050faf68481 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 11 Dec 2025 15:00:45 +0100 Subject: [PATCH 70/77] Add password method as a workaround for the necessity of leaving password as a string --- .../oracle/database/mcptoolkit/ServerConfig.java | 2 +- .../com/oracle/database/mcptoolkit/Utils.java | 2 +- .../mcptoolkit/config/DataSourceConfig.java | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java index c432dc4b..98d91cd3 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/ServerConfig.java @@ -108,7 +108,7 @@ public static ServerConfig fromSystemPropertiesAndYaml(ConfigRoot configRoot, St DataSourceConfig src = sources.get(defaultSourceKey); dbUrl = src.toJdbcUrl(); dbUser = src.user; - dbPass = src.password; + dbPass = src.getPasswordChars(); defaultSourceName = defaultSourceKey; } diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index 0901a568..fb7bec68 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -203,7 +203,7 @@ private static DataSource getOrCreateDataSource(ServerConfig cfg, String sourceN try { DataSourceConfig src = (cfg.sources != null) ? cfg.sources.get(name) : null; if (src == null) throw new IllegalArgumentException("Unknown source: " + name); - return createDataSource(src.toJdbcUrl(), src.user, src.password); + return createDataSource(src.toJdbcUrl(), src.user, src.getPasswordChars()); } catch (SQLException ex) { throw new RuntimeException(ex); } diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java index 9b0b8c51..72ea441c 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java @@ -44,7 +44,9 @@ public class DataSourceConfig { /** * The password to use for the database connection. */ - public char[] password; + public String password; + + private transient char[] passwordChars; /** * Returns the JDBC URL for the database connection. If the {@link #url} property is not set, @@ -71,6 +73,15 @@ public void substituteEnvVars() { this.database = EnvSubstitutor.substituteEnvVars(this.database); this.url = EnvSubstitutor.substituteEnvVars(this.url); this.user = EnvSubstitutor.substituteEnvVars(this.user); - this.password = EnvSubstitutor.substituteEnvVarsInCharArray(this.password); + this.passwordChars = EnvSubstitutor.substituteEnvVarsInCharArray(this.getPasswordChars()); + } + + public char[] getPasswordChars() { + if (passwordChars == null && password != null) { + passwordChars = password.toCharArray(); + // To reduce exposure + password = null; + } + return passwordChars; } } \ No newline at end of file From 894e38de5aa8648bfd991d4c7ef1d9b7b1c0ad11 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Thu, 11 Dec 2025 15:23:03 +0100 Subject: [PATCH 71/77] Fixing conflicts and making fields public again for yaml parsing --- src/oracle-db-mcp-toolkit/Dockerfile | 6 +- src/oracle-db-mcp-toolkit/README.md | 76 +++++++++++-------- src/oracle-db-mcp-toolkit/pom.xml | 9 ++- .../database/mcptoolkit/LoadedConstants.java | 2 +- .../mcptoolkit/OracleDatabaseMCPToolkit.java | 13 +++- .../com/oracle/database/mcptoolkit/Utils.java | 8 +- .../mcptoolkit/oauth/TokenGenerator.java | 4 +- 7 files changed, 69 insertions(+), 49 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/Dockerfile b/src/oracle-db-mcp-toolkit/Dockerfile index 5e0285fc..2034a156 100644 --- a/src/oracle-db-mcp-toolkit/Dockerfile +++ b/src/oracle-db-mcp-toolkit/Dockerfile @@ -29,7 +29,7 @@ RUN useradd -r -u 10001 appuser && \ WORKDIR /app USER appuser -COPY --from=builder /src/target/oracle-db-toolbox-mcp-server-*.jar \ - /app/oracle-db-toolbox-mcp-server.jar +COPY --from=builder /src/target/oracle-db-mcp-toolkit-*.jar \ + /app/oracle-db-mcp-toolkit.jar -ENTRYPOINT ["java", "-jar", "/app/oracle-db-toolbox-mcp-server.jar"] \ No newline at end of file +ENTRYPOINT ["java", "-jar", "/app/oracle-db-mcp-toolkit.jar"] \ No newline at end of file diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index 655f1c32..ff9b49ae 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -1,13 +1,15 @@ # Oracle Database MCP Toolkit -## Overview +## 1. Overview Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that lets you: * Define your own custom tools via a simple YAML configuration file. * Use 8 built-in tools to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. * Optionally use **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**, when JDBC configuration is provided. -## Custom Tool Framework — Extending the MCP Server +--- + +## 2. Custom Tool Framework — Extending the MCP Server The MCP server can load both database connection definitions and custom tool definitions from a YAML configuration file. This provides a flexible and declarative way to extend the server without modifying or rebuilding the codebase. @@ -56,9 +58,11 @@ To enable YAML configuration, launch the server with: java -DconfigFile=/path/to/config.yaml -jar .jar ``` -## Built-in Tools +--- + +## 3. Built-in Tools -### Oracle JDBC Log Analysis: +### 3.1. Oracle JDBC Log Analysis: These tools operate on Oracle JDBC thin client logs: @@ -69,14 +73,14 @@ These tools operate on Oracle JDBC thin client logs: - **`list-log-files-from-directory`**: List all visible files from a specified directory, which helps the user analyze multiple files with one prompt. - **`jdbc-log-comparison`**: Compares two log files for performance metrics, errors, and network information. -### RDBMS/SQLNet Trace Analysis: +### 3.2. RDBMS/SQLNet Trace Analysis: These tools operate on RDBMS/SQLNet trace files: - **`get-rdbms-errors`**: Extracts errors from RDBMS/SQLNet trace files. - **`get-rdbms-packet-dumps`**: Extracts packet dumps for a specific connection ID. -### Vector Similarity Search +### 3.3. Vector Similarity Search * **`similarity_search`**: Perform semantic similarity search using Oracle’s vector features (`VECTOR_EMBEDDING`, `VECTOR_DISTANCE`). @@ -94,7 +98,7 @@ These tools operate on RDBMS/SQLNet trace files: * JSON array of similar rows with scores and truncated snippets. -### SQL Execution Plan Analysis +### 3.4. SQL Execution Plan Analysis * **`explain_plan`**: Generate Oracle execution plans and receive a pre-formatted LLM prompt for tuning and explanation. @@ -121,15 +125,16 @@ These tools operate on RDBMS/SQLNet trace files: --- -## Prerequisites +## 4. Installation +### 4.1. Prerequisites - **Java 17+** (JDK) - **Credentials** with permissions for your intended operations - **MCP client** (e.g., Claude Desktop) to call the tools > The server uses UCP pooling out of the box (initial/min= 1). -### Build the MCP server jar +### 4.2. Build the MCP server jar ```bash mvn clean install @@ -137,14 +142,14 @@ mvn clean install The created jar can be found in `target/oracle-db-mcp-toolkit-1.0.0.jar`. -### Transport modes (stdio vs HTTP) +### 4.3. Choose a transport mode (stdio vs HTTP) `oracle-db-mcp-toolkit` supports two transport modes: - **Stdio (default)** – the MCP client spawns the JVM process and talks over stdin/stdout - **HTTP (streamable)** – the MCP server runs as an HTTP service, and clients connect via a URL -#### Stdio mode (default) +#### 4.3.1. Stdio mode (default) This is the mode used by tools like Claude Desktop, where the client directly launches: @@ -168,7 +173,7 @@ This is the mode used by tools like Claude Desktop, where the client directly la ``` If you don’t set `-Dtransport`, the server runs in stdio mode by default. -#### HTTP mode +#### 4.3.2. HTTP mode In HTTP mode, you run the server as a standalone HTTP service and point an MCP client to it. @@ -186,7 +191,7 @@ java \ ``` This exposes the MCP endpoint at: `http://localhost:45450/mcp`. -#### Enabling HTTPS (SSL/TLS) +### 4.4. Enabling HTTPS (SSL/TLS) To enable HTTPS (SSL/TLS), specify your certificate keystore path and password using the `-DcertificatePath` and `-DcertificatePassword` options. Only PKCS12 (`.p12` or `.pfx`) keystore files are supported. You can set the HTTPS port with the `-Dhttps.port` option. @@ -194,7 +199,7 @@ You can set the HTTPS port with the `-Dhttps.port` option. ```shell -DcertificatePath=/path/to/your-certificate.p12 -DcertificatePassword=yourPassword -Dhttps.port=443 ``` -### Using HTTP from Cline +### 4.5. Using HTTP transport and Cline Cline supports streamable HTTP directly. Example: ```json @@ -208,7 +213,7 @@ Cline supports streamable HTTP directly. Example: } ``` -### Using HTTP from Claude Desktop +### 4.6. Using HTTP from Claude Desktop Claude Desktop accepts HTTPS endpoints for remote MCP servers. If your MCP server is only available over plain HTTP (e.g. http://localhost:45450/mcp), you can use the `mcp-remote` workaround: @@ -228,17 +233,17 @@ you can use the `mcp-remote` workaround: } ``` -### HTTP Authentication Configuration +### 4.7. HTTP Authentication Configuration -#### Generated Token (For Development and Testing) +#### 4.7.1. Generated Token (For Development and Testing) To enable authentication for the HTTP server, you must set the `-DenableAuthentication` system property to `true` (default value is `false`). -If it's enabled (e.g. set to `true`) the MCP Server will check if there's an environment variable called `ORACLE_DB_TOOLBOX_AUTH_TOKEN` and its value will be used as a token. +If it's enabled (e.g. set to `true`) the MCP Server will check if there's an environment variable called `ORACLE_DB_TOOLKIT_AUTH_TOKEN` and its value will be used as a token. If the environment variable is not found, then a random UUID token will be generated once per JVM session. The token would be logged at the `INFO` level. When connecting to the MCP server, the token needs to be provided in the Authorization header of each request using the `Bearer ` prefix. -#### OAuth2 Configuration +#### 4.7.2. OAuth2 Configuration In order to configure an OAuth2 server, the `-DenableAuthentication` should be enabled alongside the following system properties: @@ -246,15 +251,15 @@ In order to configure an OAuth2 server, the `-DenableAuthentication` should be e - `-DredirectOAuthToOpenID`: (default: `false`) This system property is used to as a workaround to support OAuth servers that provide `/.well-known/openid-configuration` and not `/.well-known/oauth-authorization-server`. It works by creating an `/.well-known/oauth-authorization-server` endpoint on the MCP Server that redirects to the OAuth server's `/.well-known/openid-configuration` endpoint. - `-DintrospectionEndpoint`: The OAuth2 server's introspection endpoint used to validate an access token (The OAuth2 introspection JSON response MUST contain the `active` field, e.g. `{...,"active": false,..}`). -- `-DclientId`: Client ID (e.g. `oracle-db-toolbox`) +- `-DclientId`: Client ID (e.g. `oracle-db-toolkit`) - `-DclientSecret`: Client Secret (e.g. `Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0`) - `-DallowedHosts`: (default: `*`) The value of `Access-Control-Allow-Origin` header when requesting the `/.well-known/oauth-protected-resource` endpoint (and `/.well-known/oauth-authorization-server` if `-DredirectOAuthToOpenID` is set to `true`) of the MCP Server. For more details regarding this MCP and OAuth, please see [MCP specification for authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) (or a newer version if available). -#### Examples +##### Examples -##### Enabling Authentication with OAuth2 +###### Enabling Authentication with OAuth2 ```bash java \ @@ -264,7 +269,7 @@ java \ -DenableAuthentication=true \ -DauthServer=http://localhost:8080/realms/mcp \ -DintrospectionEndpoint=http://localhost:8080/realms/mcp/protocol/openid-connect/token/introspect \ - -DclientId=oracle-db-toolbox \ + -DclientId=oracle-db-toolkit \ -DclientSecret=Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0 \ -DallowedHosts=http://localhost:6274 \ -jar /oracle-db-mcp-toolkit-1.0.0.jar @@ -295,14 +300,14 @@ Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.oauth.OAuth2Configuration INFO: Authorization token generated (for testing and development use only): 0dd11948-37a3-470f-911e-4cd8b3d6f69c -Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.OracleDBToolboxMCPServer startHttpServer +Nov 25, 2025 3:30:46 PM com.oracle.database.jdbc.OracleDatabaseMCPToolkit startHttpServer INFO: [oracle-db-mcp-toolkit] HTTP transport started on http://localhost:45450 (endpoint: http://localhost:45450/mcp) ``` -If `ORACLE_DB_TOOLBOX_AUTH_TOKEN` environment variable is set: +If `ORACLE_DB_TOOLKIT_AUTH_TOKEN` environment variable is set: ```bash -export ORACLE_DB_TOOLBOX_AUTH_TOKEN=Secret_DeV_T0ken +export ORACLE_DB_TOOLKIT_AUTH_TOKEN=Secret_DeV_T0ken ``` Then the server logs will be the following: @@ -318,7 +323,9 @@ INFO: Authorization token generated (for testing and development use only): Secr Ultimately, the token must be included in the http request header (e.g. `Authorization: Bearer 0dd11948-37a3-470f-911e-4cd8b3d6f69c` or `Authorization: Bearer Secret_DeV_T0ken`). -### Supported System Properties +--- + +## 5. Supported System Properties
clientId No The client identifier for registering with the configured OAuth2 server.-DclientId=oracle-db-toolbox-DclientId=oracle-db-toolkit
clientSecret
configFile NoPath to a YAML file defining sources and tools.Path to a YAML file defining datasources and tools. /opt/mcp/config.yaml
@@ -435,7 +442,7 @@ Ultimately, the token must be included in the http request header (e.g. `Authori - + @@ -467,17 +474,19 @@ If you enable **only** the Log Analyzer tools, you can omit db.url. * Note: If you’re using token-based authentication (e.g., IAM tokens) or a centralized configuration provided via the JARs you place in `-Dojdbc.ext.dir`, you can omit `db.user` and `db.password`. The driver will pick up credentials and security settings from those extensions. -## Docker Image +--- + +## 6. Docker Image A `Dockerfile` is included at the root of the project so you can build and run the MCP server as a container. -### Build the image +### 6.1. Build the image From the project root (where the Dockerfile lives): ```bash podman build -t oracle-db-mcp-toolkit:1.0.0 . ``` -### Run the container (HTTP mode example) +### 6.2. Run the container (HTTP mode example) This example runs the MCP server over HTTP and HTTPS inside the container and exposes it on port 45450 and 45451 on your host. ```bash @@ -501,7 +510,8 @@ This exposes the MCP endpoint at: http://[your-ip-address]:45450/mcp or https:// You can then configure Cline or Claude Desktop as described in the Using HTTP from Cline / Claude Desktop sections above. -If you need extra JDBC / security jars (e.g. `oraclepki`, `wallets`, `centralized config`), +If you need extra JDBC / security jars (e.g. `oraclepki`, wallets, centralized config, or providers that fetch full +database credentials such as username, password, and connection string from a vault secret), mount them and point `ojdbc.ext.dir` at that directory: ```bash @@ -522,7 +532,7 @@ podman run --rm \ oracle-db-mcp-toolkit:1.0.0 ``` -### Using Docker/Podman with stdio +### 6.3. Using Docker/Podman with stdio Instead of running the MCP server over HTTP, you can keep using the **stdio** transport and let your MCP client spawn the container (via **podman run**) instead of spawning java directly. In this mode, the MCP client talks to the server over stdin/stdout, just like with a local JAR. diff --git a/src/oracle-db-mcp-toolkit/pom.xml b/src/oracle-db-mcp-toolkit/pom.xml index 2c1ccecf..9191e511 100644 --- a/src/oracle-db-mcp-toolkit/pom.xml +++ b/src/oracle-db-mcp-toolkit/pom.xml @@ -8,9 +8,12 @@ oracle-db-mcp-toolkit1.0.0jar - Oracle JDBC Log Analyzer MCP Server - The Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that enables users to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files using built-in tools. - It also offers database-powered tools, such as vector similarity search and SQL execution plan analysis. Additionally, users can define custom tools through a YAML configuration file. + Oracle Database MCP Toolkit + The Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that + enables users to define custom tools through a YAML configuration file, analyze Oracle + JDBC thin client logs and RDBMS/SQLNet trace files using built-in tools. + It also offers database-powered tools, such as vector similarity search and SQL execution + plan analysis.https://github.com/oracle/mcp/tree/main/src/oracle-db-mcp-toolkit diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java index 79ab238e..4cc309f9 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/LoadedConstants.java @@ -38,7 +38,7 @@ private LoadedConstants() {} // Prevent instantiation public static final String ALLOWED_HOSTS= System.getProperty("allowedHosts","*"); public static final String REDIRECT_OPENID_TO_OAUTH= System.getProperty("redirectOpenIDToOAuth","false"); public static final boolean ENABLE_AUTH = Boolean.parseBoolean(System.getProperty("enableAuthentication","false")); - public static final String ORACLE_DB_TOOLBOX_AUTH_TOKEN = System.getenv("ORACLE_DB_TOOLBOX_AUTH_TOKEN"); + public static final String ORACLE_DB_TOOLKIT_AUTH_TOKEN = System.getenv("ORACLE_DB_TOOLKIT_AUTH_TOKEN"); public static final String AUTH_SERVER = System.getProperty("authServer"); public static final String INTROSPECTION_ENDPOINT = System.getProperty("introspectionEndpoint"); public static final String CLIENT_ID = System.getProperty("clientId"); diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java index 55cababf..df1eeb6d 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/OracleDatabaseMCPToolkit.java @@ -1,3 +1,10 @@ +/* + ** Oracle Database MCP Toolkit version 1.0.0 + ** + ** Copyright (c) 2025 Oracle and/or its affiliates. + ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ + package com.oracle.database.mcptoolkit; import com.fasterxml.jackson.databind.ObjectMapper; @@ -50,7 +57,7 @@ public static void main(String[] args) { case "stdio" -> { server = McpServer .sync(new StdioServerTransportProvider(new ObjectMapper())) - .serverInfo("oracle-db-toolbox-mcp-server", "1.0.0") + .serverInfo("oracle-db-mcp-toolkit", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder() .tools(true) .logging() @@ -81,7 +88,7 @@ private static McpSyncServer startHttpServer() { McpSyncServer server = McpServer .sync(transport) - .serverInfo("oracle-db-toolbox-mcp-server", "1.0.0") + .serverInfo("oracle-db-mcp-toolkit", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder() .tools(true) .logging() @@ -133,7 +140,7 @@ private static McpSyncServer startHttpServer() { tomcat.start(); - LOG.info(() -> "[oracle-db-toolbox-mcp-server] HTTP transport started on " + LoadedConstants.HTTP_PORT + " (endpoint: /mcp)"); + LOG.info(() -> "[oracle-db-mcp-toolkit] HTTP transport started on " + LoadedConstants.HTTP_PORT + " (endpoint: /mcp)"); return server; } catch (Exception e) { diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index fb7bec68..47db6578 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -282,7 +282,7 @@ static void installExternalExtensionsFromDir() { final Path root = Paths.get(dir); if (!Files.isDirectory(root)) { - LOG.warning("[oracle-db-toolbox-mcp-server] ojdbc.ext.dir is not a directory: " + dir); + LOG.warning("[oracle-db-mcp-toolkit] ojdbc.ext.dir is not a directory: " + dir); return; } final List jarUrls = new ArrayList<>(); @@ -292,16 +292,16 @@ static void installExternalExtensionsFromDir() { try { jarUrls.add(p.toUri().toURL()); } catch (Exception e) { - LOG.log(Level.WARNING, "[oracle-db-toolbox-mcp-server] Failed to add jar: " + p, e); + LOG.log(Level.WARNING, "[oracle-db-mcp-toolkit] Failed to add jar: " + p, e); } }); } catch (Exception e) { - LOG.log(Level.WARNING, "[oracle-db-toolbox-mcp-server] Failed to scan " + dir, e); + LOG.log(Level.WARNING, "[oracle-db-mcp-toolkit] Failed to scan " + dir, e); return; } if (jarUrls.isEmpty()) { - LOG.warning("[oracle-db-toolbox-mcp-server] No jars found under " + dir); + LOG.warning("[oracle-db-mcp-toolkit] No jars found under " + dir); return; } diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java index 0e4a02d4..a7a82d81 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/oauth/TokenGenerator.java @@ -7,7 +7,7 @@ package com.oracle.database.mcptoolkit.oauth; -import static com.oracle.database.mcptoolkit.LoadedConstants.ORACLE_DB_TOOLBOX_AUTH_TOKEN; +import static com.oracle.database.mcptoolkit.LoadedConstants.ORACLE_DB_TOOLKIT_AUTH_TOKEN; import java.util.UUID; import java.util.logging.Level; @@ -31,7 +31,7 @@ public class TokenGenerator { * Initializes the generated token based on the {@code ORACLE_DB_TOOLBOX_AUTH_TOKEN} environment variable or a random UUID. */ private TokenGenerator() { - generatedToken = ORACLE_DB_TOOLBOX_AUTH_TOKEN != null ? ORACLE_DB_TOOLBOX_AUTH_TOKEN : UUID.randomUUID().toString() ; + generatedToken = ORACLE_DB_TOOLKIT_AUTH_TOKEN != null ? ORACLE_DB_TOOLKIT_AUTH_TOKEN : UUID.randomUUID().toString() ; LOG.log(Level.INFO, "Authorization token generated (for testing and development use only): {0}", generatedToken); } From 4e4228b104112e5af323730f6deb9aba406754f6 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Fri, 12 Dec 2025 15:38:59 +0100 Subject: [PATCH 72/77] Adding DEMO.md --- src/oracle-db-mcp-toolkit/DEMO.md | 112 ++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/oracle-db-mcp-toolkit/DEMO.md diff --git a/src/oracle-db-mcp-toolkit/DEMO.md b/src/oracle-db-mcp-toolkit/DEMO.md new file mode 100644 index 00000000..34040b1f --- /dev/null +++ b/src/oracle-db-mcp-toolkit/DEMO.md @@ -0,0 +1,112 @@ +# Oracle Database MCP Toolkit Demo + +## 1.Overview + +To test the capabilities of the Oracle Database MCP Toolkit, a demo instance of the MCP server is made available via + with the following tools activated: + +JDBC log analysis tools: + +- **`get-jdbc-stats`**: Extracts performance statistics including error counts, sent/received packets and byte counts. +- **`get-jdbc-queries`**: Retrieves all executed SQL queries with timestamps and execution times. +- **`get-jdbc-errors`**: Extracts all errors reported by both server and client. +- **`get-jdbc-connections-events`**: Shows connection open/close events. +- **`list-log-files-from-directory`**: List all visible files from a specified directory, which helps the user analyze multiple files with one prompt. +- **`jdbc-log-comparison`**: Compares two log files for performance metrics, errors, and network information. + +RDBMS/SQLNet trace analysis Tools: + +- **`get-rdbms-errors`**: Extracts errors from RDBMS/SQLNet trace files. +- **`get-rdbms-packet-dumps`**: Extracts packet dumps for a specific connection ID. + +Custom tools (created using YAML configuration file): + +- **`hotels-by-name`**: Return the details of a hotel given its name. The details include the capacity, rating and address. + +## 2. Requirements + +An MCP Client that support Streamable HTTP transport mode is needed, such as MCP Inspector, Cline or Claude Desktop. + +**Note**: If you're using Claude Desktop, you also need [mcp-remote](https://www.npmjs.com/package/mcp-remote). + +## 3. Setup + +The deployed instance uses `streamableHttp` transport protocol and a runtime generated `Authorization` token. + +Use the following token `3e297077-f01e-4045-a9d0-2a71e97e6dfa`. + +### MCP Inspector + +To use MCP Inspector as an MCP client, specify `streamableHttp`as transport type, `https://mcptoolkit.orcl.dev:45453/mcp` as the URL, _Via Proxy_ as Connection Type, +for Authentication, add a `Authorization` custom header with `Bearer 3e297077-f01e-4045-a9d0-2a71e97e6dfa` as value. +the final configuration should look as shown below: + +MCP Inspector config screenshot + +After checking the configuration, click the *Connect* button, and the available tools will be shown in the main section: + +MCP Inspector tools screenshot + +_Note :_ The filePath should be provided as a URL. + +### Cline + +Add or merge this configuration into `cline_mcp_settings.json`: + +```json +{ + "mcpServers": { + "Oracle Database MCP Toolkit (Demo)": { + "autoApprove": [], + "disabled": false, + "timeout": 60, + "type": "streamableHttp", + "url": "https://mcptoolkit.orcl.dev:45453/mcp", + "headers": { + "Authorization": "Bearer 3e297077-f01e-4045-a9d0-2a71e97e6dfa" + } + } + } +} +``` + +After saving the configuration file, the available tools will be shown in the *Configure* Tab of *MCP Servers* settings: + +Cline tools screenshot + +Here's an example of a prompt that trigger the `get-jdbc-queries` tool: + +Cline prompt example screenshot + +### Claude Desktop + +In order to connect the MCP server and also to provide the `Authorization` token to Claude Desktop, The [mcp-remote](https://www.npmjs.com/package/mcp-remote) is used to properly configure. +Below is an example of `claude_desktop_config.json` file: + +```json +{ + "mcpServers": { + "Oracle Database MCP Toolkit (Demo)": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "https://mcptoolkit.orcl.dev:45453/mcp", + "--header", + "Authorization:${DEMO_TOKEN}" + ], + "env": { + "DEMO_TOKEN": "Bearer 3e297077-f01e-4045-a9d0-2a71e97e6dfa" + } + } + } +} +``` + +Upon saving the configuration file an opening Claude Desktop, you'll be to see the tools in the *Connectors* section: + +Claude Desktop tools screenshot + +Here's the result of the same prompt used to know what queries were executed : + +Claude Desktop prompt example screenshot \ No newline at end of file From b30969d71f36c1b9cb783f45977473ad0063b8fd Mon Sep 17 00:00:00 2001 From: Jean de Lavarene Date: Mon, 15 Dec 2025 17:13:37 +0100 Subject: [PATCH 73/77] Add diagram --- src/oracle-db-mcp-toolkit/README.md | 8 ++++++-- .../images/MCPToolkitArchitectureDiagram.svg | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/oracle-db-mcp-toolkit/images/MCPToolkitArchitectureDiagram.svg diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index ff9b49ae..daa35ccd 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -4,8 +4,12 @@ Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that lets you: * Define your own custom tools via a simple YAML configuration file. - * Use 8 built-in tools to analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. - * Optionally use **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**, when JDBC configuration is provided. + * Use built-in tools: + * Analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. + * **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**. + * Deploy locally or remotely - optionally as a container - with support for TLS and OAuth2 + +![MCP Toolkit Architecture Diagram](./images/MCPToolkitArchitectureDiagram.svg) --- diff --git a/src/oracle-db-mcp-toolkit/images/MCPToolkitArchitectureDiagram.svg b/src/oracle-db-mcp-toolkit/images/MCPToolkitArchitectureDiagram.svg new file mode 100644 index 00000000..2fbb2ee0 --- /dev/null +++ b/src/oracle-db-mcp-toolkit/images/MCPToolkitArchitectureDiagram.svg @@ -0,0 +1,4 @@ + + + +
📄 YAML Config File
(Oracle data sources, custom tools,
env variables)
🔐 SSL Certificate (.p12)
(Used for HTTPS)
🖥️ STDIO Mode
• Command + Args
🌐 StreamableHTTP Mode
• URL + (optional OAuth)
🔀 Transport Layer
• STDIO Handler
• HTTP/HTTPS Handler
🧩 Tooling Engine
• Built-in Tools
• Custom Tools
• YAML-defined Sources
STDIOHTTP/HTTPSConfiguration LoadSSL/HTTPS Setup
MCP Client
MCP Server
Authentication Server
Authenticate
\ No newline at end of file From a0a92ed081ad3c2ba2d7551f0c6c69769a8713a8 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Tue, 16 Dec 2025 09:57:18 +0100 Subject: [PATCH 74/77] Add the YAML configuration file that creates the hotels-by-name custom tool --- src/oracle-db-mcp-toolkit/DEMO.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/oracle-db-mcp-toolkit/DEMO.md b/src/oracle-db-mcp-toolkit/DEMO.md index 34040b1f..0dd7c0db 100644 --- a/src/oracle-db-mcp-toolkit/DEMO.md +++ b/src/oracle-db-mcp-toolkit/DEMO.md @@ -22,6 +22,27 @@ RDBMS/SQLNet trace analysis Tools: Custom tools (created using YAML configuration file): - **`hotels-by-name`**: Return the details of a hotel given its name. The details include the capacity, rating and address. +This tool is created using the following YAML configuration file: + +```yaml +dataSources: + dev-db: + url: ${db_url} + user: ${user} + password: ${password} + +tools: + hotels-by-name: + dataSource: dev-db + description: Return the details of a hotel given its name. The details include the capacity, rating and address. + parameters: + - name: name + type: string + description: Hotel name to search for. + statement: SELECT * FROM hotels WHERE name LIKE '%' || :name || '%' +``` + +Where `${db_url}`, `${user}` and `${password}`are environment variables. ## 2. Requirements From 663ce1e993384d493ae5b3f6667cedf19fcf3bc5 Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Tue, 16 Dec 2025 11:32:07 +0100 Subject: [PATCH 75/77] Updating README.md and DEMO.md with more details --- src/oracle-db-mcp-toolkit/DEMO.md | 6 +- src/oracle-db-mcp-toolkit/README.md | 85 ++++++++++++------- .../mcptoolkit/config/DataSourceConfig.java | 2 +- 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/DEMO.md b/src/oracle-db-mcp-toolkit/DEMO.md index 0dd7c0db..cec8ff44 100644 --- a/src/oracle-db-mcp-toolkit/DEMO.md +++ b/src/oracle-db-mcp-toolkit/DEMO.md @@ -21,8 +21,8 @@ RDBMS/SQLNet trace analysis Tools: Custom tools (created using YAML configuration file): -- **`hotels-by-name`**: Return the details of a hotel given its name. The details include the capacity, rating and address. -This tool is created using the following YAML configuration file: +- **`hotels-by-name`**: Returns the details of a hotel given its name. The details include the capacity, rating and address. + This tool is created using the following YAML configuration file: ```yaml dataSources: @@ -34,7 +34,7 @@ dataSources: tools: hotels-by-name: dataSource: dev-db - description: Return the details of a hotel given its name. The details include the capacity, rating and address. + description: Returns the details of a hotel given its name. The details include the capacity, rating and address. parameters: - name: name type: string diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index daa35ccd..eecc654f 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -2,12 +2,12 @@ ## 1. Overview -Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that lets you: - * Define your own custom tools via a simple YAML configuration file. - * Use built-in tools: - * Analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. - * **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**. - * Deploy locally or remotely - optionally as a container - with support for TLS and OAuth2 +Oracle Database MCP Toolkit is a Model Context Protocol (MCP) server that lets you: +* Define your own custom tools via a simple YAML configuration file. +* Use built-in tools: + * Analyze Oracle JDBC thin client logs and RDBMS/SQLNet trace files. + * **database-powered tools**, including **vector similarity search** and **SQL execution plan analysis**. +* Deploy locally or remotely - optionally as a container - with support for TLS and OAuth2 ![MCP Toolkit Architecture Diagram](./images/MCPToolkitArchitectureDiagram.svg) @@ -19,9 +19,27 @@ This provides a flexible and declarative way to extend the server without modify A YAML file may define: -* One or more **datasources:** — named database configurations (URL, user, password, etc.) - -* One or more **tools** — each with parameters, SQL statements, and optional metadata +* **datasources:** — Database configuration info: + * `url`: This the JDBC URL used by the MCP server to connect to the database using the JDBC driver. + * `user`: The username to use for the database connection. + * `password`: The password to use for the database connection. + * `host` (optional): The hostname or IP address of the database server. + * `port` (optional): The port number on which the database server is listening. + * `database` (optional): The Oracle service name of the database. + +* One or more **tools** — The MCP tools: + * `dataSource` (optional): Defines the data source to be used (defaults to system properties `db.url`, `db.user` and `db.password`). + * `name`: The tool name and title, derived from the YAML key. + * `description`: A brief description of the tool. + * `parameters` (optional): A list of the parameters required for the tool. (To fill the statement's placeholders) + * `statement` The SQL statement to be executed by the tool. + +* If you add **parameters**, you can add the following fields: + * `name`: The name of the tool parameter. + * `type`: The data type to respect when the LLM fills the parameter. + * `description`: The description to know what this parameter is about. + * `required` (optional): Indicates whether the tool parameter is required. (default: false) + * All the parameter fields are being used to generate an InputSchema. ### DataSource Resolution Logic @@ -31,9 +49,9 @@ When executing a tool, the MCP server determines which datasource to use based o 2. If the tool does not specify a datasource, the server looks for a default datasource: - * First, it checks whether a datasource was provided via system properties (db.url, db.user, db.password) (Higher priority). +* First, it checks whether a datasource was provided via system properties (db.url, db.user, db.password) (Higher priority). - * If no system property datasource is available, it falls back to the first datasource defined in the YAML file, if present. +* If no system property datasource is available, it falls back to the first datasource defined in the YAML file, if present. 3. If no datasource can be resolved and the tool requires one (e.g., SQL-based tools), the server reports a configuration error. @@ -44,12 +62,13 @@ This design ensures that tools always have a predictable datasource while giving dataSources: prod-db: url: jdbc:oracle:thin:@prod-host:1521/ORCLPDB1 - user: ADMIN + user: ${user} password: ${password} tools: hotels-by-name: dataSource: prod-db + description: Returns the details of a hotel given its name. The details include the capacity, rating and address. parameters: - name: name type: string @@ -90,17 +109,17 @@ These tools operate on RDBMS/SQLNet trace files: **Inputs:** - * `question` (string, required): Natural language query. - * `topK` (integer, optional, default: 5): Number of closest results. - * `table` (string, default: `profile_oracle`): Table containing text + vector embeddings. - * `dataColumn` (string, default: `text`): Text/CLOB column. - * `embeddingColumn` (string, default: `embedding`): Vector column. - * `modelName` (string, default: `doc_model`): Name of the DB vector model. - * `textFetchLimit` (integer, default: 4000): Max length of returned text. + * `question` (string, required): Natural language query. + * `topK` (integer, optional, default: 5): Number of closest results. + * `table` (string, default: `profile_oracle`): Table containing text + vector embeddings. + * `dataColumn` (string, default: `text`): Text/CLOB column. + * `embeddingColumn` (string, default: `embedding`): Vector column. + * `modelName` (string, default: `doc_model`): Name of the DB vector model. + * `textFetchLimit` (integer, default: 4000): Max length of returned text. **Returns:** - * JSON array of similar rows with scores and truncated snippets. + * JSON array of similar rows with scores and truncated snippets. ### 3.4. SQL Execution Plan Analysis @@ -108,24 +127,24 @@ These tools operate on RDBMS/SQLNet trace files: **Modes:** - * `static` — Uses `EXPLAIN PLAN` (estimated plan; does not run the SQL). - * `dynamic` — Uses `DBMS_XPLAN.DISPLAY_CURSOR` for the **actual** plan of a cursor. + * `static` — Uses `EXPLAIN PLAN` (estimated plan; does not run the SQL). + * `dynamic` — Uses `DBMS_XPLAN.DISPLAY_CURSOR` for the **actual** plan of a cursor. **Inputs:** - * `sql` (required): SQL query to analyze. - * `mode` (static|dynamic, default: static) - * `execute` (boolean): Execute SQL to obtain a cursor in dynamic mode. - * `maxRows` (integer, default: 1): Limit rows fetched during execution. - * `xplanOptions` (string): Formatting options. + * `sql` (required): SQL query to analyze. + * `mode` (static|dynamic, default: static) + * `execute` (boolean): Execute SQL to obtain a cursor in dynamic mode. + * `maxRows` (integer, default: 1): Limit rows fetched during execution. + * `xplanOptions` (string): Formatting options. - * Default dynamic: `ALLSTATS LAST +PEEKED_BINDS +OUTLINE +PROJECTION` - * Default static: `BASIC +OUTLINE +PROJECTION +ALIAS` + * Default dynamic: `ALLSTATS LAST +PEEKED_BINDS +OUTLINE +PROJECTION` + * Default static: `BASIC +OUTLINE +PROJECTION +ALIAS` **Returns:** - * `planText`: DBMS_XPLAN output. - * `llmPrompt`: A structured prompt for an LLM to explain + tune the plan. + * `planText`: DBMS_XPLAN output. + * `llmPrompt`: A structured prompt for an LLM to explain + tune the plan. --- @@ -199,7 +218,7 @@ This exposes the MCP endpoint at: `http://localhost:45450/mcp`. To enable HTTPS (SSL/TLS), specify your certificate keystore path and password using the `-DcertificatePath` and `-DcertificatePassword` options. Only PKCS12 (`.p12` or `.pfx`) keystore files are supported. You can set the HTTPS port with the `-Dhttps.port` option. -##### Example +##### Example ```shell -DcertificatePath=/path/to/your-certificate.p12 -DcertificatePassword=yourPassword -Dhttps.port=443 ``` @@ -253,7 +272,7 @@ In order to configure an OAuth2 server, the `-DenableAuthentication` should be e - `-DauthServer`: The OAuth2 server URL which MUST provide the `/.well-known/oauth-authorization-server`. But if the authorization server only provides the `/.well-known/openid-configuration` you can enable `-DredirectOAuthToOpenID`. - `-DredirectOAuthToOpenID`: (default: `false`) This system property is used to as a workaround to support OAuth servers that provide `/.well-known/openid-configuration` and not `/.well-known/oauth-authorization-server`. -It works by creating an `/.well-known/oauth-authorization-server` endpoint on the MCP Server that redirects to the OAuth server's `/.well-known/openid-configuration` endpoint. + It works by creating an `/.well-known/oauth-authorization-server` endpoint on the MCP Server that redirects to the OAuth server's `/.well-known/openid-configuration` endpoint. - `-DintrospectionEndpoint`: The OAuth2 server's introspection endpoint used to validate an access token (The OAuth2 introspection JSON response MUST contain the `active` field, e.g. `{...,"active": false,..}`). - `-DclientId`: Client ID (e.g. `oracle-db-toolkit`) - `-DclientSecret`: Client Secret (e.g. `Xj9mPqR2vL5kN8tY3hB7wF4uD6cA1eZ0`) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java index 72ea441c..89dc07b4 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/config/DataSourceConfig.java @@ -26,7 +26,7 @@ public class DataSourceConfig { public String port; /** - * The Oracle SID or service name of the database. + * The Oracle service name of the database. */ public String database; From 8d4b5ce63f44536632470fdda0f5e096e85c464f Mon Sep 17 00:00:00 2001 From: Ayoub Aarrasse Date: Wed, 17 Dec 2025 13:32:58 +0100 Subject: [PATCH 76/77] Enabling keep alive and adding validation to ucp (#21) --- .../src/main/java/com/oracle/database/mcptoolkit/Utils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java index 47db6578..d61c7a1c 100644 --- a/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java +++ b/src/oracle-db-mcp-toolkit/src/main/java/com/oracle/database/mcptoolkit/Utils.java @@ -223,6 +223,8 @@ private static DataSource createDataSource(String url, String user, char[] passw pds.setConnectionProperty("remarksReporting", "true"); pds.setConnectionProperty("oracle.jdbc.vectorDefaultGetObjectType", "double[]"); pds.setConnectionProperty("oracle.jdbc.jsonDefaultGetObjectType", "java.lang.String"); + pds.setConnectionProperty("oracle.net.keepAlive", "true"); + pds.setValidateConnectionOnBorrow(true); return pds; } From b39b1473d2493c13c707c033c5ef3d56c4158cd4 Mon Sep 17 00:00:00 2001 From: Youssef Erradi Date: Mon, 22 Dec 2025 11:24:28 +0100 Subject: [PATCH 77/77] Fix 'get-jdbc-connection-events' tool name - update DEMO.md --- src/oracle-db-mcp-toolkit/DEMO.md | 2 -- src/oracle-db-mcp-toolkit/README.md | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/oracle-db-mcp-toolkit/DEMO.md b/src/oracle-db-mcp-toolkit/DEMO.md index cec8ff44..136dc768 100644 --- a/src/oracle-db-mcp-toolkit/DEMO.md +++ b/src/oracle-db-mcp-toolkit/DEMO.md @@ -10,8 +10,6 @@ JDBC log analysis tools: - **`get-jdbc-stats`**: Extracts performance statistics including error counts, sent/received packets and byte counts. - **`get-jdbc-queries`**: Retrieves all executed SQL queries with timestamps and execution times. - **`get-jdbc-errors`**: Extracts all errors reported by both server and client. -- **`get-jdbc-connections-events`**: Shows connection open/close events. -- **`list-log-files-from-directory`**: List all visible files from a specified directory, which helps the user analyze multiple files with one prompt. - **`jdbc-log-comparison`**: Compares two log files for performance metrics, errors, and network information. RDBMS/SQLNet trace analysis Tools: diff --git a/src/oracle-db-mcp-toolkit/README.md b/src/oracle-db-mcp-toolkit/README.md index eecc654f..9f771d28 100644 --- a/src/oracle-db-mcp-toolkit/README.md +++ b/src/oracle-db-mcp-toolkit/README.md @@ -92,7 +92,7 @@ These tools operate on Oracle JDBC thin client logs: - **`get-jdbc-stats`**: Extracts performance statistics including error counts, sent/received packets and byte counts. - **`get-jdbc-queries`**: Retrieves all executed SQL queries with timestamps and execution times. - **`get-jdbc-errors`**: Extracts all errors reported by both server and client. -- **`get-jdbc-connections-events`**: Shows connection open/close events. +- **`get-jdbc-connection-events`**: Shows connection open/close events. - **`list-log-files-from-directory`**: List all visible files from a specified directory, which helps the user analyze multiple files with one prompt. - **`jdbc-log-comparison`**: Compares two log files for performance metrics, errors, and network information.
clientId No The client identifier for registering with the configured OAuth2 server.-DclientId=oracle-db-toolbox-DclientId=oracle-db-toolkit
clientSecret