frankfurtermcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ FASTMCP_SERVER_HOST = "0.0.0.0"
2
+ FASTMCP_SERVER_PORT = 8000
3
+ FASTMCP_SERVER_DEBUG = False
4
+ FASTMCP_SERVER_LOG_LEVEL = "INFO"
5
+
6
+ MCP_SERVER_TRANSPORT = "streamable-http"
7
+ FRANKFURTER_API_URL = "https://api.frankfurter.dev/v1"
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "uv" # See documentation for possible values
9
+ directory: "/" # Location of package manifests
10
+ schedule:
11
+ interval: "weekly"
@@ -0,0 +1,30 @@
1
+ name: pypi-publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ release-build:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v5
19
+
20
+ - name: Install the project
21
+ run: uv sync --locked --all-extras --dev
22
+
23
+ - name: Build release distributions
24
+ run: |
25
+ uv build
26
+
27
+ - name: Upload distributions
28
+ run: |
29
+ # Requires UV_PUBLISH_TOKEN to be set
30
+ uv publish
@@ -0,0 +1,31 @@
1
+ on:
2
+ push:
3
+ branches: [master]
4
+ paths:
5
+ - "**.py"
6
+ pull_request:
7
+ branches: [master]
8
+ paths:
9
+ - "**.py"
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ name: pytest
15
+
16
+ jobs:
17
+ uv-pytest:
18
+ name: python
19
+ runs-on: ubuntu-latest
20
+
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v5
26
+
27
+ - name: Install the project
28
+ run: uv sync --locked --all-extras --dev
29
+
30
+ - name: Run tests using pytest
31
+ run: uv run --group test pytest tests/
@@ -0,0 +1,162 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mypy
141
+ .mypy_cache/
142
+ .dmypy.json
143
+ dmypy.json
144
+
145
+ # Pyre type checker
146
+ .pyre/
147
+
148
+ # pytype static type analyzer
149
+ .pytype/
150
+
151
+ # Cython debug symbols
152
+ cython_debug/
153
+
154
+ # Visual Studio Code workspace settings
155
+ # .vscode/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
@@ -0,0 +1,34 @@
1
+ default_language_version:
2
+ python: python3
3
+
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: v5.0.0
7
+ hooks:
8
+ - id: trailing-whitespace
9
+ exclude: tests
10
+ - id: end-of-file-fixer
11
+ - id: check-merge-conflict
12
+ - id: check-case-conflict
13
+ - id: check-json
14
+ - id: check-toml
15
+ exclude: tests/fixtures/invalid_lock/uv\.lock
16
+ - id: check-yaml
17
+ - id: pretty-format-json
18
+ args: [--autofix, --no-ensure-ascii, --no-sort-keys]
19
+ - id: check-ast
20
+ - id: debug-statements
21
+ - id: check-docstring-first
22
+ - id: detect-private-key
23
+ - repo: https://github.com/astral-sh/ruff-pre-commit
24
+ # Ruff version.
25
+ rev: v0.11.13
26
+ hooks:
27
+ # Run the linter.
28
+ - id: ruff
29
+ args: [--fix, --exit-non-zero-on-fix, --respect-gitignore]
30
+ exclude: ".*uv.lock|.*_static"
31
+ # Run the formatter.
32
+ - id: ruff-format
33
+ args: [--respect-gitignore]
34
+ exclude: ".*uv.lock|.*_static"
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025--present Anirban Basu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: frankfurtermcp
3
+ Version: 0.1.0
4
+ Summary: A MCP server for the Frankfurter API for currency exchange rates.
5
+ Author-email: Anirban Basu <anirbanbasu@users.noreply.github.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: fastmcp>=2.7.0
10
+ Requires-Dist: python-dotenv>=1.1.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue?logo=python&logoColor=3776ab&labelColor=e4e4e4)](https://www.python.org/downloads/release/python-3120/)
14
+ [![Experimental status](https://img.shields.io/badge/Status-experimental-orange)](#)
15
+
16
+ [![Dependabot Updates](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/dependabot/dependabot-updates) [![pytest](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/uv-pytest.yml/badge.svg)](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/uv-pytest.yml)
17
+ # Frankfurter MCP
18
+
19
+ [Frankfurter](https://frankfurter.dev/) is a useful API for latest currency exchange rates, historical data, or time series published by sources such as the European Central Bank. Should you need to access the Frankfurter API as tools for language model agents exposed over the Model Context Protocol (MCP), Frankfurter MCP is what you need.
20
+
21
+ ## Project status
22
+
23
+ Following is a table of some updates regarding the project status. Note that these do not correspond to specific commits or milestones.
24
+
25
+ | Date | Status | Notes or observations |
26
+ |----------|:-------------:|----------------------|
27
+ | June 8, 2025 | active | Added dynamic composition.<br/>**TODO**: Exception handling; outgoing proxy and self-signed certificate configuration; Dockerisation. |
28
+ | June 7, 2025 | active | Added tools to cover all the functionalities of the Frankfurter API. |
29
+ | June 7, 2025 | active | Project started. |
30
+
31
+ ## Installation
32
+
33
+ The directory where you clone this repository will be referred to as the _working directory_ or _WD_ hereinafter.
34
+
35
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/). To install the project with its essential dependencies in a virtual environment, run the following in the _WD_. To install all non-essential dependencies (_which are required for developing and testing_), add the `--all-extras` flag to the following command.
36
+
37
+
38
+ ```bash
39
+ uv sync
40
+ ```
41
+
42
+ ## Environment variables
43
+
44
+ Following is a list of environment variables that can be used to configure the application. A template of environment variables is provided in the file `.env.template`.
45
+
46
+ The following environment variables can be specified, prefixed with `FASTMCP_SERVER_`: `HOST`, `PORT`, `DEBUG` and `LOG_LEVEL`. See [key configuration options](https://gofastmcp.com/servers/fastmcp#key-configuration-options) for FastMCP. Note that `on_duplicate_` prefixed options specified as environment variables _will be ignored_.
47
+
48
+ | Variable | [Default value] and description |
49
+ |--------------|----------------|
50
+ | `MCP_SERVER_TRANSPORT` | [streamable-http] The acceptable options are `stdio`, `sse` or `streamable-http`. Given the use-case of running this MCP server as a remotely accessible endpoint, there is no real reason to choose `stdio`. |
51
+ | `FRANKFURTER_API_URL` | [https://api.frankfurter.dev/v1] If you are [self-hosting the Frankfurter API](https://hub.docker.com/r/lineofflight/frankfurter), you should change this to the API endpoint address of your deployment. |
52
+
53
+ ## Usage (self-hosted server using `uv`)
54
+
55
+ Copy the `.env.template` file to a `.env` file in the _WD_, to modify the aforementioned environment variables, if you want to use anything other than the default settings.
56
+
57
+ Run the following in the _WD_ to start the MCP server.
58
+
59
+ ```bash
60
+ uv run frankfurtermcp
61
+ ```
62
+
63
+ If you want to run it without `uv`, assuming that the appropriate virtual environment has been created in the `.venv` within the _WD_, you can start the server calling the following.
64
+
65
+ ```bash
66
+ ./.venv/bin/python -m frankfurtermcp.server
67
+ ```
68
+
69
+ If you are installing this packag using `pip` in a virtual environment (possibly managed by `conda` or `pyenv`), then running `python -m frankfurtermcp.server` in that virtual environment.
70
+
71
+ The MCP endpoint will be available over HTTP at [http://localhost:8000/sse](http://localhost:8000/sse) for the Server Sent Events (SSE) transport, or [http://localhost:8000/mcp](http://localhost:8000/mcp) for the streamable HTTP transport. To exit the server, use the Ctrl+C key combination.
72
+
73
+ ## Usage (self-hosted server using Docker)
74
+
75
+ **TODO:** To be added, eventually.
76
+
77
+ ## Usage (dynamic mounting with FastMCP)
78
+
79
+ To see how to use the MCP server by mounting it dynamically with [FastMCP](https://gofastmcp.com/), check the file `src/frankfurtermcp/composition.py`.
80
+
81
+ ## Contributing
82
+
83
+ Install [`pre-commit`](https://pre-commit.com/) for Git and [`ruff`](https://docs.astral.sh/ruff/installation/). Then enable `pre-commit` by running the following in the _WD_.
84
+
85
+ ```bash
86
+ pre-commit install
87
+ ```
88
+ Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
89
+
90
+ ## Testing
91
+
92
+ To run the provided test cases, execute the following. Add the flag `--capture=tee-sys` to the command to display further console output.
93
+
94
+ _Note that for the tests to succeed, the environment variable `MCP_SERVER_TRANSPORT` must be set to either `sse` or `streamable-http`, or not set at all, in which case it will default to `streamable-http`_.
95
+
96
+ ```bash
97
+ uv run --group test pytest -q tests/
98
+ ```
99
+
100
+ ## License
101
+
102
+ [MIT](https://choosealicense.com/licenses/mit/).
@@ -0,0 +1,90 @@
1
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue?logo=python&logoColor=3776ab&labelColor=e4e4e4)](https://www.python.org/downloads/release/python-3120/)
2
+ [![Experimental status](https://img.shields.io/badge/Status-experimental-orange)](#)
3
+
4
+ [![Dependabot Updates](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/dependabot/dependabot-updates) [![pytest](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/uv-pytest.yml/badge.svg)](https://github.com/anirbanbasu/frankfurtermcp/actions/workflows/uv-pytest.yml)
5
+ # Frankfurter MCP
6
+
7
+ [Frankfurter](https://frankfurter.dev/) is a useful API for latest currency exchange rates, historical data, or time series published by sources such as the European Central Bank. Should you need to access the Frankfurter API as tools for language model agents exposed over the Model Context Protocol (MCP), Frankfurter MCP is what you need.
8
+
9
+ ## Project status
10
+
11
+ Following is a table of some updates regarding the project status. Note that these do not correspond to specific commits or milestones.
12
+
13
+ | Date | Status | Notes or observations |
14
+ |----------|:-------------:|----------------------|
15
+ | June 8, 2025 | active | Added dynamic composition.<br/>**TODO**: Exception handling; outgoing proxy and self-signed certificate configuration; Dockerisation. |
16
+ | June 7, 2025 | active | Added tools to cover all the functionalities of the Frankfurter API. |
17
+ | June 7, 2025 | active | Project started. |
18
+
19
+ ## Installation
20
+
21
+ The directory where you clone this repository will be referred to as the _working directory_ or _WD_ hereinafter.
22
+
23
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/). To install the project with its essential dependencies in a virtual environment, run the following in the _WD_. To install all non-essential dependencies (_which are required for developing and testing_), add the `--all-extras` flag to the following command.
24
+
25
+
26
+ ```bash
27
+ uv sync
28
+ ```
29
+
30
+ ## Environment variables
31
+
32
+ Following is a list of environment variables that can be used to configure the application. A template of environment variables is provided in the file `.env.template`.
33
+
34
+ The following environment variables can be specified, prefixed with `FASTMCP_SERVER_`: `HOST`, `PORT`, `DEBUG` and `LOG_LEVEL`. See [key configuration options](https://gofastmcp.com/servers/fastmcp#key-configuration-options) for FastMCP. Note that `on_duplicate_` prefixed options specified as environment variables _will be ignored_.
35
+
36
+ | Variable | [Default value] and description |
37
+ |--------------|----------------|
38
+ | `MCP_SERVER_TRANSPORT` | [streamable-http] The acceptable options are `stdio`, `sse` or `streamable-http`. Given the use-case of running this MCP server as a remotely accessible endpoint, there is no real reason to choose `stdio`. |
39
+ | `FRANKFURTER_API_URL` | [https://api.frankfurter.dev/v1] If you are [self-hosting the Frankfurter API](https://hub.docker.com/r/lineofflight/frankfurter), you should change this to the API endpoint address of your deployment. |
40
+
41
+ ## Usage (self-hosted server using `uv`)
42
+
43
+ Copy the `.env.template` file to a `.env` file in the _WD_, to modify the aforementioned environment variables, if you want to use anything other than the default settings.
44
+
45
+ Run the following in the _WD_ to start the MCP server.
46
+
47
+ ```bash
48
+ uv run frankfurtermcp
49
+ ```
50
+
51
+ If you want to run it without `uv`, assuming that the appropriate virtual environment has been created in the `.venv` within the _WD_, you can start the server calling the following.
52
+
53
+ ```bash
54
+ ./.venv/bin/python -m frankfurtermcp.server
55
+ ```
56
+
57
+ If you are installing this packag using `pip` in a virtual environment (possibly managed by `conda` or `pyenv`), then running `python -m frankfurtermcp.server` in that virtual environment.
58
+
59
+ The MCP endpoint will be available over HTTP at [http://localhost:8000/sse](http://localhost:8000/sse) for the Server Sent Events (SSE) transport, or [http://localhost:8000/mcp](http://localhost:8000/mcp) for the streamable HTTP transport. To exit the server, use the Ctrl+C key combination.
60
+
61
+ ## Usage (self-hosted server using Docker)
62
+
63
+ **TODO:** To be added, eventually.
64
+
65
+ ## Usage (dynamic mounting with FastMCP)
66
+
67
+ To see how to use the MCP server by mounting it dynamically with [FastMCP](https://gofastmcp.com/), check the file `src/frankfurtermcp/composition.py`.
68
+
69
+ ## Contributing
70
+
71
+ Install [`pre-commit`](https://pre-commit.com/) for Git and [`ruff`](https://docs.astral.sh/ruff/installation/). Then enable `pre-commit` by running the following in the _WD_.
72
+
73
+ ```bash
74
+ pre-commit install
75
+ ```
76
+ Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
77
+
78
+ ## Testing
79
+
80
+ To run the provided test cases, execute the following. Add the flag `--capture=tee-sys` to the command to display further console output.
81
+
82
+ _Note that for the tests to succeed, the environment variable `MCP_SERVER_TRANSPORT` must be set to either `sse` or `streamable-http`, or not set at all, in which case it will default to `streamable-http`_.
83
+
84
+ ```bash
85
+ uv run --group test pytest -q tests/
86
+ ```
87
+
88
+ ## License
89
+
90
+ [MIT](https://choosealicense.com/licenses/mit/).
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "frankfurtermcp"
3
+ version = "0.1.0"
4
+ description = "A MCP server for the Frankfurter API for currency exchange rates."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [
8
+ { name = "Anirban Basu", email = "anirbanbasu@users.noreply.github.com" }
9
+ ]
10
+ requires-python = ">=3.12"
11
+ dependencies = [
12
+ "fastmcp>=2.7.0",
13
+ "python-dotenv>=1.1.0",
14
+ ]
15
+
16
+ [project.scripts]
17
+ frankfurtermcp = "frankfurtermcp.server:main"
18
+
19
+ [build-system]
20
+ requires = ["hatchling"]
21
+ build-backend = "hatchling.build"
22
+
23
+ [dependency-groups]
24
+ dev = [
25
+ "icecream>=2.1.4",
26
+ ]
27
+ test = [
28
+ "pytest>=8.4.0",
29
+ ]
@@ -0,0 +1,14 @@
1
+ from dotenv import load_dotenv
2
+
3
+ from importlib.metadata import metadata
4
+ from frankfurtermcp.common import AppMetadata, EnvironmentVariables, ic
5
+ from frankfurtermcp.utils import parse_env
6
+
7
+ package_metadata = metadata(AppMetadata.PACKAGE_NAME)
8
+
9
+ frankfurter_api_url = parse_env(
10
+ EnvironmentVariables.FRANKFURTER_API_URL,
11
+ default_value=EnvironmentVariables.DEFAULT__FRANKFURTER_API_URL,
12
+ )
13
+
14
+ ic(load_dotenv())
@@ -0,0 +1,25 @@
1
+ try:
2
+ from icecream import ic
3
+ except ImportError: # Graceful fallback if IceCream isn't installed.
4
+ ic = lambda *a: None if not a else (a[0] if len(a) == 1 else a) # noqa
5
+
6
+
7
+ class EnvironmentVariables:
8
+ """
9
+ List of environment variables used in this project.
10
+ """
11
+
12
+ MCP_SERVER_TRANSPORT = "MCP_SERVER_TRANSPORT"
13
+ DEFAULT__MCP_SERVER_TRANSPORT = "streamable-http"
14
+ ALLOWED__MCP_SERVER_TRANSPORT = ["stdio", "sse", "streamable-http"]
15
+
16
+ FRANKFURTER_API_URL = "FRANKFURTER_API_URL"
17
+ DEFAULT__FRANKFURTER_API_URL = "https://api.frankfurter.dev/v1"
18
+
19
+
20
+ class AppMetadata:
21
+ """
22
+ Metadata for the application.
23
+ """
24
+
25
+ PACKAGE_NAME = "frankfurtermcp"
@@ -0,0 +1,61 @@
1
+ import signal
2
+ import sys
3
+ from fastmcp import FastMCP
4
+ from frankfurtermcp.common import EnvironmentVariables
5
+ from frankfurtermcp.server import app as frankfurtermcp
6
+ from frankfurtermcp.utils import parse_env
7
+
8
+ app = FastMCP(
9
+ name="test_composition",
10
+ description="This is a MCP server to test dynamic composition of MCP.",
11
+ tags=["frankfurtermcp", "mcp", "composition"],
12
+ )
13
+
14
+ COMPOSITION_PREFIX = "composition_"
15
+
16
+
17
+ @app.tool(
18
+ description="The quintessential hello world tool",
19
+ tags=["hello", "world"],
20
+ name="hello_world",
21
+ )
22
+ def hello_world(name: str = None) -> str:
23
+ """
24
+ A simple tool that returns a greeting message.
25
+
26
+ Args:
27
+ name (str): The name to greet.
28
+
29
+ Returns:
30
+ str: A greeting message.
31
+ """
32
+ suffix = "This is the MCP server to test dynamic composition."
33
+ return f"Hello, {name}! {suffix}" if name else f"Hello World! {suffix}"
34
+
35
+
36
+ def main():
37
+ """
38
+ Main function to run the MCP server.
39
+ """
40
+
41
+ def sigterm_handler(signal, frame):
42
+ """
43
+ Signal handler to shut down the server gracefully.
44
+ """
45
+ # This is absolutely necessary to exit the program
46
+ sys.exit(0)
47
+
48
+ signal.signal(signal.SIGTERM, sigterm_handler)
49
+
50
+ app.mount(prefix=COMPOSITION_PREFIX, server=frankfurtermcp, as_proxy=False)
51
+ app.run(
52
+ transport=parse_env(
53
+ EnvironmentVariables.MCP_SERVER_TRANSPORT,
54
+ default_value=EnvironmentVariables.DEFAULT__MCP_SERVER_TRANSPORT,
55
+ allowed_values=EnvironmentVariables.ALLOWED__MCP_SERVER_TRANSPORT,
56
+ )
57
+ )
58
+
59
+
60
+ if __name__ == "__main__":
61
+ main()