perfume-mcp 2.4.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,3 @@
1
+ __pycache__
2
+ *.egg-info
3
+ dist
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 10ML
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,177 @@
1
+ Metadata-Version: 2.5
2
+ Name: perfume-mcp
3
+ Version: 2.4.0
4
+ Summary: Perfume MCP server: free fragrance knowledge for Claude, Cursor and any MCP client. 5,400+ perfumes, note pyramids, accords, similar scents and alternatives from the 10ML Fragrance Index. No API key.
5
+ Project-URL: Homepage, https://mcp.10ml.com
6
+ Project-URL: Documentation, https://mcp.10ml.com
7
+ Author-email: 10ML Team <contact@10ml.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: chatgpt,claude,cologne,cursor,eau-de-parfum,fragrance,fragrance-api,fragrance-mcp,fragrance-notes,mcp,mcp-server,model-context-protocol,perfume,perfume-api,perfume-mcp,perfume-notes,perfume-recommendation,scent,similar-perfumes
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: mcp<2,>=1.12
18
+ Description-Content-Type: text/markdown
19
+
20
+ <!-- mcp-name: com.10ml/perfume-finder -->
21
+ # 10ML Perfume Finder: free perfume MCP server and perfume API
22
+
23
+ Free fragrance knowledge for AI assistants: 5,400+ perfumes and 249 houses from the 10ML Fragrance Index, with note pyramids, accords, similar scents and alternatives. No API key, no sign-up, read-only.
24
+
25
+ [![Official MCP Registry](https://img.shields.io/badge/MCP%20Registry-com.10ml%2Fperfume--finder-002AFF)](https://registry.modelcontextprotocol.io/v0/servers?search=com.10ml/perfume-finder)
26
+ [![PyPI](https://img.shields.io/pypi/v/perfume-mcp?color=002AFF)](https://pypi.org/project/perfume-mcp/)
27
+
28
+ This package (`perfume-mcp`) runs the 10ML Fragrance Index over **stdio**, for clients that start local commands. The same server answers directly over Streamable HTTP at `https://mcp.10ml.com/mcp`, with nothing to install.
29
+
30
+ ## Connect in 10 seconds
31
+
32
+ **Claude, ChatGPT and other clients that take a URL** (nothing to install): add the connector URL `https://mcp.10ml.com/mcp`. In Claude: Settings, Connectors, Add custom connector. In Claude Code:
33
+
34
+ ```bash
35
+ claude mcp add --transport http perfume-finder https://mcp.10ml.com/mcp
36
+ ```
37
+
38
+ **Claude Desktop, Cursor, Windsurf, Cline, Zed and other clients that start a local command** use this package:
39
+
40
+ ```bash
41
+ uvx perfume-mcp
42
+ ```
43
+
44
+ Or install it with `pipx install perfume-mcp` or `pip install perfume-mcp` and run `perfume-mcp`.
45
+
46
+ Claude Desktop (`Settings, Developer, Edit Config`) and Cursor (`~/.cursor/mcp.json`) take the same block:
47
+
48
+ ```json
49
+ {
50
+ "mcpServers": {
51
+ "perfume-finder": {
52
+ "command": "uvx",
53
+ "args": [
54
+ "perfume-mcp"
55
+ ]
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ Goose (`goose configure`, Add Extension, Command-line Extension): command `uvx perfume-mcp`.
62
+
63
+ ## Why agents pick it
64
+
65
+ - Free, with no API key, no account and no sign-in.
66
+ - 15 read-only tools with output schemas, 6 prompts and resources, in ten languages.
67
+ - Curated by 10ML's fragrance editors: every perfume has its note pyramid (top, heart, base), accords, family, seasons and occasions.
68
+ - In our 40-question benchmark of free, no-key perfume MCP servers (20 October 2026): 39 of 40 correct, median 232 ms. [Read the benchmark](https://10ml.com/perfume-mcp-servers-compared).
69
+
70
+ ## Tools (15, all read-only)
71
+
72
+ | Tool | What it does | Example question |
73
+ |---|---|---|
74
+ | `search_perfumes` | Find perfumes and fragrances by name, notes, accords, family, audience, season, occasion or style. | "A fresh citrus summer cologne for men" |
75
+ | `get_perfume` | The full profile of one perfume: note pyramid, accords, seasons, similar scents. | "What notes are in Delina?" |
76
+ | `compare_perfumes` | Two to four perfumes side by side: shared notes, differences, which is fresher or longer lasting. | "Delina or Layton for a winter evening?" |
77
+ | `find_similar_perfumes` | Perfumes that smell like one you love, or like a list of notes. Steer fresher, sweeter or darker. | "Smells like Sauvage but fresher" |
78
+ | `find_perfume_alternatives` | The editors' alternatives to a famous perfume, from other houses. | "An alternative to Baccarat Rouge 540" |
79
+ | `recommend_perfumes` | Picks from what you love, dislike and avoid, with the reason for each. | "I love Delina and vanilla, nothing too sweet, for winter evenings" |
80
+ | `perfumes_by_note` | Perfumes built around a note, strongest first. | "Perfumes with oud" |
81
+ | `explain_note` | What a note smells like, what it pairs with and where it leads. | "What does ambroxan smell like?" |
82
+ | `get_house` | A perfume house: country, style, what it is known for, signature perfumes. | "Tell me about Creed" |
83
+ | `list_houses` | Browse perfume houses by type or country. | "Which niche houses are from Italy?" |
84
+ | `explain_perfume_term` | Perfume vocabulary and the fragrance families. | "What is the difference between EDT and EDP?" |
85
+ | `suggest_layering` | Which notes or perfumes layer well, and how to spray them. | "How do I layer vanilla and citrus?" |
86
+ | `pick_for_occasion` | Perfumes for a season, an occasion, an audience and a style. | "A fresh summer office scent for men" |
87
+ | `find_gift` | Gift ideas from who it is for and what they love. | "A gift for a woman who loves Delina" |
88
+ | `search_guides` | 10ML perfume guides and articles to read or cite, in ten languages. | "How do I make perfume last longer?" |
89
+
90
+ ## REST quick start
91
+
92
+ The same index is available as plain JSON over GET (OpenAPI 3.1: https://mcp.10ml.com/openapi.json; output shown trimmed to the main fields):
93
+
94
+ ```bash
95
+ curl -s "https://mcp.10ml.com/api/v1/alternatives?perfume=Baccarat+Rouge+540&limit=2"
96
+ ```
97
+
98
+ ```json
99
+ {
100
+ "total": 6,
101
+ "original": {
102
+ "name": "Maison Francis Kurkdjian Baccarat Rouge 540",
103
+ "id": "maison-francis-kurkdjian-baccarat-rouge-540-edp"
104
+ },
105
+ "alternatives": [
106
+ {
107
+ "name": "Maison Alhambra Baroque Rouge Extrait",
108
+ "closeness": "very close",
109
+ "why": "Sugared amber, saffron and airy woods aim at the same luminous effect.",
110
+ "id": "maison-alhambra-baroque-rouge-extrait",
111
+ "house": "Maison Alhambra",
112
+ "source": "editors"
113
+ },
114
+ {
115
+ "name": "Dossier Ambery Saffron",
116
+ "closeness": "similar vibe",
117
+ "why": "Saffron and amber with sweet woods share the glowing amber idea.",
118
+ "id": "dossier-ambery-saffron",
119
+ "house": "Dossier",
120
+ "source": "editors"
121
+ }
122
+ ],
123
+ "source": "10ML Fragrance Index"
124
+ }
125
+ ```
126
+
127
+ A typed client for it: `perfume-api` on [npm](https://www.npmjs.com/package/perfume-api) and [PyPI](https://pypi.org/project/perfume-api/).
128
+
129
+ ## Example answers
130
+
131
+ **"What is a good alternative to Baccarat Rouge 540 from another house?"** (`find_perfume_alternatives`)
132
+ Maison Alhambra Baroque Rouge Extrait (Maison Alhambra), very close: Sugared amber, saffron and airy woods aim at the same luminous effect.
133
+
134
+ **"A fresh citrus summer cologne for men."** (`search_perfumes`)
135
+ Ciao Citrus (Paris Corner): Grapefruit and bergamot over orange blossom and lily of the valley, drying to musk, sandalwood and amber. Also: Citrus Batikanga (Maison Crivelli): Bitter orange, kalamansi and bergamot are pressed fresh, then a pinch of chili and a vetiver-cedar base keep it lively.
136
+
137
+ **"What does vanilla smell like?"** (`explain_note`)
138
+ vanilla, a gourmand note: Warm, creamy, sweet and comforting, like custard and cured pods. Pairs with tonka bean, amber, sandalwood, tobacco.
139
+
140
+ ## Languages
141
+
142
+ Every tool takes `locale` (`en`, `de`, `fr`, `es`, `it`, `ro`, `el`, `hu`, `cs`, `pl`: English, German, French, Spanish, Italian, Romanian, Greek, Hungarian, Czech, Polish) for the 10ml.com links in the answer, and `response_format` (`concise` or `detailed`). Notes are understood in other languages too: "Vanille" finds vanilla.
143
+
144
+ ## Limits and fair use
145
+
146
+ Free and read-only, with no key or sign-in. There is no published rate limit: please cache answers, page long lists with `limit` and `cursor`, and send a descriptive User-Agent from automated clients. For heavy use write to contact@10ml.com.
147
+
148
+ ## How it compares
149
+
150
+ In our 40-question benchmark of free, no-key perfume MCP servers (20 October 2026): 39 of 40 correct, median 232 ms. [Read the benchmark](https://10ml.com/perfume-mcp-servers-compared).
151
+
152
+ ## FAQ
153
+
154
+ **What is a perfume MCP server?**
155
+ An MCP server lets an AI assistant such as Claude, ChatGPT or Cursor call tools. A perfume MCP server gives it perfume knowledge: notes, accords, similar scents, alternatives and houses. This one is free and needs no API key.
156
+
157
+ **Is there a free perfume API?**
158
+ Yes. The same index is available as plain JSON over HTTPS at https://mcp.10ml.com/api/v1 with an OpenAPI 3.1 description at https://mcp.10ml.com/openapi.json. No API key, no sign-up. The `perfume-api` packages on npm and PyPI wrap it in a typed client.
159
+
160
+ **Do I need an API key or an account?**
161
+ No. It is free, read-only and needs no key, account or sign-in.
162
+
163
+ **How do I find a perfume similar to another one?**
164
+ Ask your assistant "what smells like Baccarat Rouge 540?" and it calls `find_similar_perfumes`, which ranks perfumes by shared notes, accords and style and says how close each one is (very close, similar vibe, same spirit). Over REST: `GET /api/v1/similar?perfume=Baccarat+Rouge+540`.
165
+
166
+ **Is there a fragrance notes API?**
167
+ Yes. `GET /api/v1/notes/vanilla` explains a note (what it smells like, family, origin, pairings), and `GET /api/v1/perfumes?notes_include=vanilla,oud` lists perfumes built around notes. Every perfume profile carries its top, heart and base notes.
168
+
169
+ ## Cite as
170
+
171
+ Cite the answers as "the 10ML Fragrance Index", curated by the fragrance editors of [10ML](https://10ml.com).
172
+
173
+ [mcp.10ml.com](https://mcp.10ml.com) · [10ml.com/perfume-mcp-server](https://10ml.com/perfume-mcp-server) · [Privacy](https://mcp.10ml.com/privacy) · [Terms](https://mcp.10ml.com/terms) · contact@10ml.com
174
+
175
+ ## License
176
+
177
+ MIT (the packages). Registry name: `com.10ml/perfume-finder`.
@@ -0,0 +1,158 @@
1
+ <!-- mcp-name: com.10ml/perfume-finder -->
2
+ # 10ML Perfume Finder: free perfume MCP server and perfume API
3
+
4
+ Free fragrance knowledge for AI assistants: 5,400+ perfumes and 249 houses from the 10ML Fragrance Index, with note pyramids, accords, similar scents and alternatives. No API key, no sign-up, read-only.
5
+
6
+ [![Official MCP Registry](https://img.shields.io/badge/MCP%20Registry-com.10ml%2Fperfume--finder-002AFF)](https://registry.modelcontextprotocol.io/v0/servers?search=com.10ml/perfume-finder)
7
+ [![PyPI](https://img.shields.io/pypi/v/perfume-mcp?color=002AFF)](https://pypi.org/project/perfume-mcp/)
8
+
9
+ This package (`perfume-mcp`) runs the 10ML Fragrance Index over **stdio**, for clients that start local commands. The same server answers directly over Streamable HTTP at `https://mcp.10ml.com/mcp`, with nothing to install.
10
+
11
+ ## Connect in 10 seconds
12
+
13
+ **Claude, ChatGPT and other clients that take a URL** (nothing to install): add the connector URL `https://mcp.10ml.com/mcp`. In Claude: Settings, Connectors, Add custom connector. In Claude Code:
14
+
15
+ ```bash
16
+ claude mcp add --transport http perfume-finder https://mcp.10ml.com/mcp
17
+ ```
18
+
19
+ **Claude Desktop, Cursor, Windsurf, Cline, Zed and other clients that start a local command** use this package:
20
+
21
+ ```bash
22
+ uvx perfume-mcp
23
+ ```
24
+
25
+ Or install it with `pipx install perfume-mcp` or `pip install perfume-mcp` and run `perfume-mcp`.
26
+
27
+ Claude Desktop (`Settings, Developer, Edit Config`) and Cursor (`~/.cursor/mcp.json`) take the same block:
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "perfume-finder": {
33
+ "command": "uvx",
34
+ "args": [
35
+ "perfume-mcp"
36
+ ]
37
+ }
38
+ }
39
+ }
40
+ ```
41
+
42
+ Goose (`goose configure`, Add Extension, Command-line Extension): command `uvx perfume-mcp`.
43
+
44
+ ## Why agents pick it
45
+
46
+ - Free, with no API key, no account and no sign-in.
47
+ - 15 read-only tools with output schemas, 6 prompts and resources, in ten languages.
48
+ - Curated by 10ML's fragrance editors: every perfume has its note pyramid (top, heart, base), accords, family, seasons and occasions.
49
+ - In our 40-question benchmark of free, no-key perfume MCP servers (20 October 2026): 39 of 40 correct, median 232 ms. [Read the benchmark](https://10ml.com/perfume-mcp-servers-compared).
50
+
51
+ ## Tools (15, all read-only)
52
+
53
+ | Tool | What it does | Example question |
54
+ |---|---|---|
55
+ | `search_perfumes` | Find perfumes and fragrances by name, notes, accords, family, audience, season, occasion or style. | "A fresh citrus summer cologne for men" |
56
+ | `get_perfume` | The full profile of one perfume: note pyramid, accords, seasons, similar scents. | "What notes are in Delina?" |
57
+ | `compare_perfumes` | Two to four perfumes side by side: shared notes, differences, which is fresher or longer lasting. | "Delina or Layton for a winter evening?" |
58
+ | `find_similar_perfumes` | Perfumes that smell like one you love, or like a list of notes. Steer fresher, sweeter or darker. | "Smells like Sauvage but fresher" |
59
+ | `find_perfume_alternatives` | The editors' alternatives to a famous perfume, from other houses. | "An alternative to Baccarat Rouge 540" |
60
+ | `recommend_perfumes` | Picks from what you love, dislike and avoid, with the reason for each. | "I love Delina and vanilla, nothing too sweet, for winter evenings" |
61
+ | `perfumes_by_note` | Perfumes built around a note, strongest first. | "Perfumes with oud" |
62
+ | `explain_note` | What a note smells like, what it pairs with and where it leads. | "What does ambroxan smell like?" |
63
+ | `get_house` | A perfume house: country, style, what it is known for, signature perfumes. | "Tell me about Creed" |
64
+ | `list_houses` | Browse perfume houses by type or country. | "Which niche houses are from Italy?" |
65
+ | `explain_perfume_term` | Perfume vocabulary and the fragrance families. | "What is the difference between EDT and EDP?" |
66
+ | `suggest_layering` | Which notes or perfumes layer well, and how to spray them. | "How do I layer vanilla and citrus?" |
67
+ | `pick_for_occasion` | Perfumes for a season, an occasion, an audience and a style. | "A fresh summer office scent for men" |
68
+ | `find_gift` | Gift ideas from who it is for and what they love. | "A gift for a woman who loves Delina" |
69
+ | `search_guides` | 10ML perfume guides and articles to read or cite, in ten languages. | "How do I make perfume last longer?" |
70
+
71
+ ## REST quick start
72
+
73
+ The same index is available as plain JSON over GET (OpenAPI 3.1: https://mcp.10ml.com/openapi.json; output shown trimmed to the main fields):
74
+
75
+ ```bash
76
+ curl -s "https://mcp.10ml.com/api/v1/alternatives?perfume=Baccarat+Rouge+540&limit=2"
77
+ ```
78
+
79
+ ```json
80
+ {
81
+ "total": 6,
82
+ "original": {
83
+ "name": "Maison Francis Kurkdjian Baccarat Rouge 540",
84
+ "id": "maison-francis-kurkdjian-baccarat-rouge-540-edp"
85
+ },
86
+ "alternatives": [
87
+ {
88
+ "name": "Maison Alhambra Baroque Rouge Extrait",
89
+ "closeness": "very close",
90
+ "why": "Sugared amber, saffron and airy woods aim at the same luminous effect.",
91
+ "id": "maison-alhambra-baroque-rouge-extrait",
92
+ "house": "Maison Alhambra",
93
+ "source": "editors"
94
+ },
95
+ {
96
+ "name": "Dossier Ambery Saffron",
97
+ "closeness": "similar vibe",
98
+ "why": "Saffron and amber with sweet woods share the glowing amber idea.",
99
+ "id": "dossier-ambery-saffron",
100
+ "house": "Dossier",
101
+ "source": "editors"
102
+ }
103
+ ],
104
+ "source": "10ML Fragrance Index"
105
+ }
106
+ ```
107
+
108
+ A typed client for it: `perfume-api` on [npm](https://www.npmjs.com/package/perfume-api) and [PyPI](https://pypi.org/project/perfume-api/).
109
+
110
+ ## Example answers
111
+
112
+ **"What is a good alternative to Baccarat Rouge 540 from another house?"** (`find_perfume_alternatives`)
113
+ Maison Alhambra Baroque Rouge Extrait (Maison Alhambra), very close: Sugared amber, saffron and airy woods aim at the same luminous effect.
114
+
115
+ **"A fresh citrus summer cologne for men."** (`search_perfumes`)
116
+ Ciao Citrus (Paris Corner): Grapefruit and bergamot over orange blossom and lily of the valley, drying to musk, sandalwood and amber. Also: Citrus Batikanga (Maison Crivelli): Bitter orange, kalamansi and bergamot are pressed fresh, then a pinch of chili and a vetiver-cedar base keep it lively.
117
+
118
+ **"What does vanilla smell like?"** (`explain_note`)
119
+ vanilla, a gourmand note: Warm, creamy, sweet and comforting, like custard and cured pods. Pairs with tonka bean, amber, sandalwood, tobacco.
120
+
121
+ ## Languages
122
+
123
+ Every tool takes `locale` (`en`, `de`, `fr`, `es`, `it`, `ro`, `el`, `hu`, `cs`, `pl`: English, German, French, Spanish, Italian, Romanian, Greek, Hungarian, Czech, Polish) for the 10ml.com links in the answer, and `response_format` (`concise` or `detailed`). Notes are understood in other languages too: "Vanille" finds vanilla.
124
+
125
+ ## Limits and fair use
126
+
127
+ Free and read-only, with no key or sign-in. There is no published rate limit: please cache answers, page long lists with `limit` and `cursor`, and send a descriptive User-Agent from automated clients. For heavy use write to contact@10ml.com.
128
+
129
+ ## How it compares
130
+
131
+ In our 40-question benchmark of free, no-key perfume MCP servers (20 October 2026): 39 of 40 correct, median 232 ms. [Read the benchmark](https://10ml.com/perfume-mcp-servers-compared).
132
+
133
+ ## FAQ
134
+
135
+ **What is a perfume MCP server?**
136
+ An MCP server lets an AI assistant such as Claude, ChatGPT or Cursor call tools. A perfume MCP server gives it perfume knowledge: notes, accords, similar scents, alternatives and houses. This one is free and needs no API key.
137
+
138
+ **Is there a free perfume API?**
139
+ Yes. The same index is available as plain JSON over HTTPS at https://mcp.10ml.com/api/v1 with an OpenAPI 3.1 description at https://mcp.10ml.com/openapi.json. No API key, no sign-up. The `perfume-api` packages on npm and PyPI wrap it in a typed client.
140
+
141
+ **Do I need an API key or an account?**
142
+ No. It is free, read-only and needs no key, account or sign-in.
143
+
144
+ **How do I find a perfume similar to another one?**
145
+ Ask your assistant "what smells like Baccarat Rouge 540?" and it calls `find_similar_perfumes`, which ranks perfumes by shared notes, accords and style and says how close each one is (very close, similar vibe, same spirit). Over REST: `GET /api/v1/similar?perfume=Baccarat+Rouge+540`.
146
+
147
+ **Is there a fragrance notes API?**
148
+ Yes. `GET /api/v1/notes/vanilla` explains a note (what it smells like, family, origin, pairings), and `GET /api/v1/perfumes?notes_include=vanilla,oud` lists perfumes built around notes. Every perfume profile carries its top, heart and base notes.
149
+
150
+ ## Cite as
151
+
152
+ Cite the answers as "the 10ML Fragrance Index", curated by the fragrance editors of [10ML](https://10ml.com).
153
+
154
+ [mcp.10ml.com](https://mcp.10ml.com) · [10ml.com/perfume-mcp-server](https://10ml.com/perfume-mcp-server) · [Privacy](https://mcp.10ml.com/privacy) · [Terms](https://mcp.10ml.com/terms) · contact@10ml.com
155
+
156
+ ## License
157
+
158
+ MIT (the packages). Registry name: `com.10ml/perfume-finder`.
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "perfume-mcp"
7
+ version = "2.4.0"
8
+ description = "Perfume MCP server: free fragrance knowledge for Claude, Cursor and any MCP client. 5,400+ perfumes, note pyramids, accords, similar scents and alternatives from the 10ML Fragrance Index. No API key."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "10ML Team", email = "contact@10ml.com" }]
13
+ keywords = ["perfume", "fragrance", "cologne", "perfume-api", "fragrance-api", "perfume-mcp", "fragrance-mcp", "mcp", "mcp-server", "model-context-protocol", "perfume-notes", "fragrance-notes", "scent", "eau-de-parfum", "perfume-recommendation", "similar-perfumes", "claude", "chatgpt", "cursor"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Software Development :: Libraries",
20
+ ]
21
+ dependencies = ["mcp>=1.12,<2"]
22
+
23
+ [project.urls]
24
+ Homepage = "https://mcp.10ml.com"
25
+ Documentation = "https://mcp.10ml.com"
26
+
27
+ [project.scripts]
28
+ perfume-mcp = "perfume_mcp:main"
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ packages = ["src/perfume_mcp"]
@@ -0,0 +1,84 @@
1
+ """10ML Fragrance Index: stdio front for the remote MCP server at https://mcp.10ml.com/mcp.
2
+
3
+ Clients that only start local commands (Claude Desktop, Cursor, Goose) run this with `uvx perfume-finder-mcp` or `uvx perfume-mcp` (one code base, two package names);
4
+ every request is passed straight through to the remote server and answered by it.
5
+ """
6
+ import asyncio
7
+ import sys
8
+
9
+ from mcp import ClientSession, types
10
+ from mcp.client.streamable_http import streamablehttp_client
11
+ from mcp.server.lowlevel import Server
12
+ from mcp.server.stdio import stdio_server
13
+
14
+ REMOTE_URL = "https://mcp.10ml.com/mcp"
15
+ __version__ = "2.4.0"
16
+
17
+
18
+ async def _run() -> None:
19
+ async with streamablehttp_client(REMOTE_URL) as (read, write, _):
20
+ async with ClientSession(read, write) as remote:
21
+ init = await remote.initialize()
22
+ caps = init.capabilities
23
+ info = init.serverInfo
24
+ server = Server(info.name or "10ML Fragrance Index", version=info.version or __version__, instructions=init.instructions)
25
+
26
+ if caps.tools:
27
+ @server.list_tools()
28
+ async def list_tools() -> list[types.Tool]:
29
+ return (await remote.list_tools()).tools
30
+
31
+ @server.call_tool(validate_input=False)
32
+ async def call_tool(name: str, arguments: dict | None) -> types.CallToolResult:
33
+ return await remote.call_tool(name, arguments or {})
34
+
35
+ if caps.prompts:
36
+ @server.list_prompts()
37
+ async def list_prompts() -> list[types.Prompt]:
38
+ return (await remote.list_prompts()).prompts
39
+
40
+ @server.get_prompt()
41
+ async def get_prompt(name: str, arguments: dict[str, str] | None) -> types.GetPromptResult:
42
+ return await remote.get_prompt(name, arguments)
43
+
44
+ if caps.resources:
45
+ @server.list_resources()
46
+ async def list_resources() -> list[types.Resource]:
47
+ return (await remote.list_resources()).resources
48
+
49
+ @server.list_resource_templates()
50
+ async def list_resource_templates() -> list[types.ResourceTemplate]:
51
+ return (await remote.list_resource_templates()).resourceTemplates
52
+
53
+ @server.read_resource()
54
+ async def read_resource(uri):
55
+ res = await remote.read_resource(uri)
56
+ return [
57
+ ReadResourceContents(
58
+ content=c.text if isinstance(c, types.TextResourceContents) else c.blob,
59
+ mime_type=c.mimeType,
60
+ )
61
+ for c in res.contents
62
+ ]
63
+
64
+ if caps.completions:
65
+ @server.completion()
66
+ async def completion(ref, argument, context):
67
+ res = await remote.complete(ref, {"name": argument.name, "value": argument.value}, context.arguments if context else None)
68
+ return res.completion
69
+
70
+ async with stdio_server() as (r, w):
71
+ await server.run(r, w, server.create_initialization_options())
72
+
73
+
74
+ from mcp.server.lowlevel.helper_types import ReadResourceContents # noqa: E402
75
+
76
+
77
+ def main() -> None:
78
+ try:
79
+ asyncio.run(_run())
80
+ except KeyboardInterrupt:
81
+ pass
82
+ except BaseException as err: # noqa: BLE001
83
+ print(f"10ML Fragrance Index: could not reach {REMOTE_URL}: {err}", file=sys.stderr)
84
+ sys.exit(1)
@@ -0,0 +1,3 @@
1
+ from . import main
2
+
3
+ main()