strava-activity-mcp-server 0.2.3__tar.gz → 0.2.5__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.
Potentially problematic release.
This version of strava-activity-mcp-server might be problematic. Click here for more details.
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/.python-version +1 -1
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/PKG-INFO +1 -1
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/pyproject.toml +21 -21
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/prompts.md +231 -231
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/src/strava_activity_mcp_server/__init__.py +3 -3
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/src/strava_activity_mcp_server/strava_activity_mcp_server.py +100 -13
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/uv.lock +2 -2
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/.github/workflows/python-publish.yml +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/.gitignore +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/.vscode/settings.json +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/LICENSE +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/README.md +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/auth.jpg +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/chat_1.jpg +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/chat_2.jpg +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/chat_3.jpg +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/chat_4.jpg +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/code.jpg +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/image.jpg +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/mcp_pypi_example.md +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/requirements.txt +0 -0
- {strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/src/strava_activity_mcp_server/__main__.py +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
3.13
|
|
1
|
+
3.13
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "strava-activity-mcp-server"
|
|
3
|
-
version = "0.2.
|
|
4
|
-
description = "Strava MCP server: one-time browser auth, then automatic refresh-token login"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.10"
|
|
7
|
-
dependencies = [
|
|
8
|
-
"build>=1.3.0",
|
|
9
|
-
"mcp[cli]>=1.16.0",
|
|
10
|
-
"twine>=6.2.0",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
[project.scripts]
|
|
14
|
-
strava-activity-mcp-server = "strava_activity_mcp_server:main"
|
|
15
|
-
|
|
16
|
-
[build-system]
|
|
17
|
-
requires = ["hatchling"]
|
|
18
|
-
build-backend = "hatchling.build"
|
|
19
|
-
|
|
20
|
-
[tool.hatch.build.targets.wheel]
|
|
21
|
-
packages = ["src/strava_activity_mcp_server"]
|
|
1
|
+
[project]
|
|
2
|
+
name = "strava-activity-mcp-server"
|
|
3
|
+
version = "0.2.5"
|
|
4
|
+
description = "Strava MCP server: one-time browser auth, then automatic refresh-token login"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"build>=1.3.0",
|
|
9
|
+
"mcp[cli]>=1.16.0",
|
|
10
|
+
"twine>=6.2.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
strava-activity-mcp-server = "strava_activity_mcp_server:main"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
build-backend = "hatchling.build"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/strava_activity_mcp_server"]
|
|
@@ -1,231 +1,231 @@
|
|
|
1
|
-
Title: Prompts - Model Context Protocol
|
|
2
|
-
|
|
3
|
-
URL Source: http://modelcontextprotocol.io/specification/2025-06-18/server/prompts
|
|
4
|
-
|
|
5
|
-
Markdown Content:
|
|
6
|
-
The Model Context Protocol (MCP) provides a standardized way for servers to expose prompt templates to clients. Prompts allow servers to provide structured messages and instructions for interacting with language models. Clients can discover available prompts, retrieve their contents, and provide arguments to customize them.
|
|
7
|
-
|
|
8
|
-
User Interaction Model
|
|
9
|
-
----------------------
|
|
10
|
-
|
|
11
|
-
Prompts are designed to be **user-controlled**, meaning they are exposed from servers to clients with the intention of the user being able to explicitly select them for use.Typically, prompts would be triggered through user-initiated commands in the user interface, which allows users to naturally discover and invoke available prompts.For example, as slash commands:However, implementors are free to expose prompts through any interface pattern that suits their needs—the protocol itself does not mandate any specific user interaction model.
|
|
12
|
-
|
|
13
|
-
Capabilities
|
|
14
|
-
------------
|
|
15
|
-
|
|
16
|
-
Servers that support prompts **MUST** declare the `prompts` capability during [initialization](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization):
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
{
|
|
20
|
-
"capabilities": {
|
|
21
|
-
"prompts": {
|
|
22
|
-
"listChanged": true
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`listChanged` indicates whether the server will emit notifications when the list of available prompts changes.
|
|
29
|
-
|
|
30
|
-
Protocol Messages
|
|
31
|
-
-----------------
|
|
32
|
-
|
|
33
|
-
### Listing Prompts
|
|
34
|
-
|
|
35
|
-
To retrieve available prompts, clients send a `prompts/list` request. This operation supports [pagination](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/pagination).**Request:**
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
{
|
|
39
|
-
"jsonrpc": "2.0",
|
|
40
|
-
"id": 1,
|
|
41
|
-
"method": "prompts/list",
|
|
42
|
-
"params": {
|
|
43
|
-
"cursor": "optional-cursor-value"
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
**Response:**
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
{
|
|
52
|
-
"jsonrpc": "2.0",
|
|
53
|
-
"id": 1,
|
|
54
|
-
"result": {
|
|
55
|
-
"prompts": [
|
|
56
|
-
{
|
|
57
|
-
"name": "code_review",
|
|
58
|
-
"title": "Request Code Review",
|
|
59
|
-
"description": "Asks the LLM to analyze code quality and suggest improvements",
|
|
60
|
-
"arguments": [
|
|
61
|
-
{
|
|
62
|
-
"name": "code",
|
|
63
|
-
"description": "The code to review",
|
|
64
|
-
"required": true
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
}
|
|
68
|
-
],
|
|
69
|
-
"nextCursor": "next-page-cursor"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Getting a Prompt
|
|
75
|
-
|
|
76
|
-
To retrieve a specific prompt, clients send a `prompts/get` request. Arguments may be auto-completed through [the completion API](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion).**Request:**
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
{
|
|
80
|
-
"jsonrpc": "2.0",
|
|
81
|
-
"id": 2,
|
|
82
|
-
"method": "prompts/get",
|
|
83
|
-
"params": {
|
|
84
|
-
"name": "code_review",
|
|
85
|
-
"arguments": {
|
|
86
|
-
"code": "def hello():\n print('world')"
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
**Response:**
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
{
|
|
96
|
-
"jsonrpc": "2.0",
|
|
97
|
-
"id": 2,
|
|
98
|
-
"result": {
|
|
99
|
-
"description": "Code review prompt",
|
|
100
|
-
"messages": [
|
|
101
|
-
{
|
|
102
|
-
"role": "user",
|
|
103
|
-
"content": {
|
|
104
|
-
"type": "text",
|
|
105
|
-
"text": "Please review this Python code:\ndef hello():\n print('world')"
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### List Changed Notification
|
|
114
|
-
|
|
115
|
-
When the list of available prompts changes, servers that declared the `listChanged` capability **SHOULD** send a notification:
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
{
|
|
119
|
-
"jsonrpc": "2.0",
|
|
120
|
-
"method": "notifications/prompts/list_changed"
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Message Flow
|
|
125
|
-
------------
|
|
126
|
-
|
|
127
|
-
Data Types
|
|
128
|
-
----------
|
|
129
|
-
|
|
130
|
-
### Prompt
|
|
131
|
-
|
|
132
|
-
A prompt definition includes:
|
|
133
|
-
|
|
134
|
-
* `name`: Unique identifier for the prompt
|
|
135
|
-
* `title`: Optional human-readable name of the prompt for display purposes.
|
|
136
|
-
* `description`: Optional human-readable description
|
|
137
|
-
* `arguments`: Optional list of arguments for customization
|
|
138
|
-
|
|
139
|
-
### PromptMessage
|
|
140
|
-
|
|
141
|
-
Messages in a prompt can contain:
|
|
142
|
-
|
|
143
|
-
* `role`: Either “user” or “assistant” to indicate the speaker
|
|
144
|
-
* `content`: One of the following content types:
|
|
145
|
-
|
|
146
|
-
#### Text Content
|
|
147
|
-
|
|
148
|
-
Text content represents plain text messages:
|
|
149
|
-
|
|
150
|
-
```
|
|
151
|
-
{
|
|
152
|
-
"type": "text",
|
|
153
|
-
"text": "The text content of the message"
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
This is the most common content type used for natural language interactions.
|
|
158
|
-
|
|
159
|
-
#### Image Content
|
|
160
|
-
|
|
161
|
-
Image content allows including visual information in messages:
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
{
|
|
165
|
-
"type": "image",
|
|
166
|
-
"data": "base64-encoded-image-data",
|
|
167
|
-
"mimeType": "image/png"
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
The image data **MUST** be base64-encoded and include a valid MIME type. This enables multi-modal interactions where visual context is important.
|
|
172
|
-
|
|
173
|
-
#### Audio Content
|
|
174
|
-
|
|
175
|
-
Audio content allows including audio information in messages:
|
|
176
|
-
|
|
177
|
-
```
|
|
178
|
-
{
|
|
179
|
-
"type": "audio",
|
|
180
|
-
"data": "base64-encoded-audio-data",
|
|
181
|
-
"mimeType": "audio/wav"
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
The audio data MUST be base64-encoded and include a valid MIME type. This enables multi-modal interactions where audio context is important.
|
|
186
|
-
|
|
187
|
-
#### Embedded Resources
|
|
188
|
-
|
|
189
|
-
Embedded resources allow referencing server-side resources directly in messages:
|
|
190
|
-
|
|
191
|
-
```
|
|
192
|
-
{
|
|
193
|
-
"type": "resource",
|
|
194
|
-
"resource": {
|
|
195
|
-
"uri": "resource://example",
|
|
196
|
-
"name": "example",
|
|
197
|
-
"title": "My Example Resource",
|
|
198
|
-
"mimeType": "text/plain",
|
|
199
|
-
"text": "Resource content"
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Resources can contain either text or binary (blob) data and **MUST** include:
|
|
205
|
-
|
|
206
|
-
* A valid resource URI
|
|
207
|
-
* The appropriate MIME type
|
|
208
|
-
* Either text content or base64-encoded blob data
|
|
209
|
-
|
|
210
|
-
Embedded resources enable prompts to seamlessly incorporate server-managed content like documentation, code samples, or other reference materials directly into the conversation flow.
|
|
211
|
-
|
|
212
|
-
Error Handling
|
|
213
|
-
--------------
|
|
214
|
-
|
|
215
|
-
Servers **SHOULD** return standard JSON-RPC errors for common failure cases:
|
|
216
|
-
|
|
217
|
-
* Invalid prompt name: `-32602` (Invalid params)
|
|
218
|
-
* Missing required arguments: `-32602` (Invalid params)
|
|
219
|
-
* Internal errors: `-32603` (Internal error)
|
|
220
|
-
|
|
221
|
-
Implementation Considerations
|
|
222
|
-
-----------------------------
|
|
223
|
-
|
|
224
|
-
1. Servers **SHOULD** validate prompt arguments before processing
|
|
225
|
-
2. Clients **SHOULD** handle pagination for large prompt lists
|
|
226
|
-
3. Both parties **SHOULD** respect capability negotiation
|
|
227
|
-
|
|
228
|
-
Security
|
|
229
|
-
--------
|
|
230
|
-
|
|
231
|
-
Implementations **MUST** carefully validate all prompt inputs and outputs to prevent injection attacks or unauthorized access to resources.
|
|
1
|
+
Title: Prompts - Model Context Protocol
|
|
2
|
+
|
|
3
|
+
URL Source: http://modelcontextprotocol.io/specification/2025-06-18/server/prompts
|
|
4
|
+
|
|
5
|
+
Markdown Content:
|
|
6
|
+
The Model Context Protocol (MCP) provides a standardized way for servers to expose prompt templates to clients. Prompts allow servers to provide structured messages and instructions for interacting with language models. Clients can discover available prompts, retrieve their contents, and provide arguments to customize them.
|
|
7
|
+
|
|
8
|
+
User Interaction Model
|
|
9
|
+
----------------------
|
|
10
|
+
|
|
11
|
+
Prompts are designed to be **user-controlled**, meaning they are exposed from servers to clients with the intention of the user being able to explicitly select them for use.Typically, prompts would be triggered through user-initiated commands in the user interface, which allows users to naturally discover and invoke available prompts.For example, as slash commands:However, implementors are free to expose prompts through any interface pattern that suits their needs—the protocol itself does not mandate any specific user interaction model.
|
|
12
|
+
|
|
13
|
+
Capabilities
|
|
14
|
+
------------
|
|
15
|
+
|
|
16
|
+
Servers that support prompts **MUST** declare the `prompts` capability during [initialization](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization):
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
{
|
|
20
|
+
"capabilities": {
|
|
21
|
+
"prompts": {
|
|
22
|
+
"listChanged": true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`listChanged` indicates whether the server will emit notifications when the list of available prompts changes.
|
|
29
|
+
|
|
30
|
+
Protocol Messages
|
|
31
|
+
-----------------
|
|
32
|
+
|
|
33
|
+
### Listing Prompts
|
|
34
|
+
|
|
35
|
+
To retrieve available prompts, clients send a `prompts/list` request. This operation supports [pagination](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/pagination).**Request:**
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
{
|
|
39
|
+
"jsonrpc": "2.0",
|
|
40
|
+
"id": 1,
|
|
41
|
+
"method": "prompts/list",
|
|
42
|
+
"params": {
|
|
43
|
+
"cursor": "optional-cursor-value"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Response:**
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
{
|
|
52
|
+
"jsonrpc": "2.0",
|
|
53
|
+
"id": 1,
|
|
54
|
+
"result": {
|
|
55
|
+
"prompts": [
|
|
56
|
+
{
|
|
57
|
+
"name": "code_review",
|
|
58
|
+
"title": "Request Code Review",
|
|
59
|
+
"description": "Asks the LLM to analyze code quality and suggest improvements",
|
|
60
|
+
"arguments": [
|
|
61
|
+
{
|
|
62
|
+
"name": "code",
|
|
63
|
+
"description": "The code to review",
|
|
64
|
+
"required": true
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"nextCursor": "next-page-cursor"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Getting a Prompt
|
|
75
|
+
|
|
76
|
+
To retrieve a specific prompt, clients send a `prompts/get` request. Arguments may be auto-completed through [the completion API](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion).**Request:**
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
{
|
|
80
|
+
"jsonrpc": "2.0",
|
|
81
|
+
"id": 2,
|
|
82
|
+
"method": "prompts/get",
|
|
83
|
+
"params": {
|
|
84
|
+
"name": "code_review",
|
|
85
|
+
"arguments": {
|
|
86
|
+
"code": "def hello():\n print('world')"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Response:**
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
{
|
|
96
|
+
"jsonrpc": "2.0",
|
|
97
|
+
"id": 2,
|
|
98
|
+
"result": {
|
|
99
|
+
"description": "Code review prompt",
|
|
100
|
+
"messages": [
|
|
101
|
+
{
|
|
102
|
+
"role": "user",
|
|
103
|
+
"content": {
|
|
104
|
+
"type": "text",
|
|
105
|
+
"text": "Please review this Python code:\ndef hello():\n print('world')"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### List Changed Notification
|
|
114
|
+
|
|
115
|
+
When the list of available prompts changes, servers that declared the `listChanged` capability **SHOULD** send a notification:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
{
|
|
119
|
+
"jsonrpc": "2.0",
|
|
120
|
+
"method": "notifications/prompts/list_changed"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Message Flow
|
|
125
|
+
------------
|
|
126
|
+
|
|
127
|
+
Data Types
|
|
128
|
+
----------
|
|
129
|
+
|
|
130
|
+
### Prompt
|
|
131
|
+
|
|
132
|
+
A prompt definition includes:
|
|
133
|
+
|
|
134
|
+
* `name`: Unique identifier for the prompt
|
|
135
|
+
* `title`: Optional human-readable name of the prompt for display purposes.
|
|
136
|
+
* `description`: Optional human-readable description
|
|
137
|
+
* `arguments`: Optional list of arguments for customization
|
|
138
|
+
|
|
139
|
+
### PromptMessage
|
|
140
|
+
|
|
141
|
+
Messages in a prompt can contain:
|
|
142
|
+
|
|
143
|
+
* `role`: Either “user” or “assistant” to indicate the speaker
|
|
144
|
+
* `content`: One of the following content types:
|
|
145
|
+
|
|
146
|
+
#### Text Content
|
|
147
|
+
|
|
148
|
+
Text content represents plain text messages:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
{
|
|
152
|
+
"type": "text",
|
|
153
|
+
"text": "The text content of the message"
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This is the most common content type used for natural language interactions.
|
|
158
|
+
|
|
159
|
+
#### Image Content
|
|
160
|
+
|
|
161
|
+
Image content allows including visual information in messages:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
{
|
|
165
|
+
"type": "image",
|
|
166
|
+
"data": "base64-encoded-image-data",
|
|
167
|
+
"mimeType": "image/png"
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The image data **MUST** be base64-encoded and include a valid MIME type. This enables multi-modal interactions where visual context is important.
|
|
172
|
+
|
|
173
|
+
#### Audio Content
|
|
174
|
+
|
|
175
|
+
Audio content allows including audio information in messages:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
{
|
|
179
|
+
"type": "audio",
|
|
180
|
+
"data": "base64-encoded-audio-data",
|
|
181
|
+
"mimeType": "audio/wav"
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The audio data MUST be base64-encoded and include a valid MIME type. This enables multi-modal interactions where audio context is important.
|
|
186
|
+
|
|
187
|
+
#### Embedded Resources
|
|
188
|
+
|
|
189
|
+
Embedded resources allow referencing server-side resources directly in messages:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
{
|
|
193
|
+
"type": "resource",
|
|
194
|
+
"resource": {
|
|
195
|
+
"uri": "resource://example",
|
|
196
|
+
"name": "example",
|
|
197
|
+
"title": "My Example Resource",
|
|
198
|
+
"mimeType": "text/plain",
|
|
199
|
+
"text": "Resource content"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Resources can contain either text or binary (blob) data and **MUST** include:
|
|
205
|
+
|
|
206
|
+
* A valid resource URI
|
|
207
|
+
* The appropriate MIME type
|
|
208
|
+
* Either text content or base64-encoded blob data
|
|
209
|
+
|
|
210
|
+
Embedded resources enable prompts to seamlessly incorporate server-managed content like documentation, code samples, or other reference materials directly into the conversation flow.
|
|
211
|
+
|
|
212
|
+
Error Handling
|
|
213
|
+
--------------
|
|
214
|
+
|
|
215
|
+
Servers **SHOULD** return standard JSON-RPC errors for common failure cases:
|
|
216
|
+
|
|
217
|
+
* Invalid prompt name: `-32602` (Invalid params)
|
|
218
|
+
* Missing required arguments: `-32602` (Invalid params)
|
|
219
|
+
* Internal errors: `-32603` (Internal error)
|
|
220
|
+
|
|
221
|
+
Implementation Considerations
|
|
222
|
+
-----------------------------
|
|
223
|
+
|
|
224
|
+
1. Servers **SHOULD** validate prompt arguments before processing
|
|
225
|
+
2. Clients **SHOULD** handle pagination for large prompt lists
|
|
226
|
+
3. Both parties **SHOULD** respect capability negotiation
|
|
227
|
+
|
|
228
|
+
Security
|
|
229
|
+
--------
|
|
230
|
+
|
|
231
|
+
Implementations **MUST** carefully validate all prompt inputs and outputs to prevent injection attacks or unauthorized access to resources.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .strava_activity_mcp_server import mcp
|
|
2
|
-
def main() -> None:
|
|
3
|
-
"""Run the MCP server."""
|
|
1
|
+
from .strava_activity_mcp_server import mcp
|
|
2
|
+
def main() -> None:
|
|
3
|
+
"""Run the MCP server."""
|
|
4
4
|
mcp.run()
|
|
@@ -118,8 +118,22 @@ async def get_athlete_stats(
|
|
|
118
118
|
code: str,
|
|
119
119
|
client_id: int | None = None,
|
|
120
120
|
client_secret: str | None = None,
|
|
121
|
+
after: int | None = None,
|
|
122
|
+
before: int | None = None,
|
|
123
|
+
page: int | None = None,
|
|
124
|
+
per_page: int | None = None,
|
|
121
125
|
) -> dict:
|
|
122
|
-
"""Exchange an authorization code for access + refresh tokens and get athlete activities.
|
|
126
|
+
"""Exchange an authorization code for access + refresh tokens and get athlete activities with optional filters.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
code: Authorization code from Strava OAuth
|
|
130
|
+
client_id: Strava client ID
|
|
131
|
+
client_secret: Strava client secret
|
|
132
|
+
after: An epoch timestamp to use for filtering activities that have taken place after a certain time
|
|
133
|
+
before: An epoch timestamp to use for filtering activities that have taken place before a certain time
|
|
134
|
+
page: The page of activities (default=1)
|
|
135
|
+
per_page: How many activities per page (default=30)
|
|
136
|
+
"""
|
|
123
137
|
if not code:
|
|
124
138
|
return {"error": "authorization code is required"}
|
|
125
139
|
|
|
@@ -178,7 +192,24 @@ async def get_athlete_stats(
|
|
|
178
192
|
|
|
179
193
|
# return {"tokens": tokens, "access_token": access_token, "refresh_token": refresh_token}
|
|
180
194
|
|
|
181
|
-
|
|
195
|
+
# Build URL with query parameters
|
|
196
|
+
params = []
|
|
197
|
+
if after is not None:
|
|
198
|
+
params.append(f"after={after}")
|
|
199
|
+
if before is not None:
|
|
200
|
+
params.append(f"before={before}")
|
|
201
|
+
if page is not None:
|
|
202
|
+
params.append(f"page={page}")
|
|
203
|
+
if per_page is not None:
|
|
204
|
+
params.append(f"per_page={per_page}")
|
|
205
|
+
|
|
206
|
+
# Default per_page to 30 if not specified (Strava API default)
|
|
207
|
+
if per_page is None:
|
|
208
|
+
params.append("per_page=30")
|
|
209
|
+
|
|
210
|
+
query_string = "&".join(params) if params else ""
|
|
211
|
+
url = f"https://www.strava.com/api/v3/athlete/activities?{query_string}"
|
|
212
|
+
|
|
182
213
|
headers = {
|
|
183
214
|
"accept": "application/json",
|
|
184
215
|
"authorization": f"Bearer {access_token}"
|
|
@@ -196,15 +227,44 @@ async def get_athlete_stats(
|
|
|
196
227
|
"save": save_result
|
|
197
228
|
}
|
|
198
229
|
|
|
199
|
-
return response.json()
|
|
200
|
-
|
|
201
230
|
@mcp.tool("strava://athlete/stats-with-token")
|
|
202
|
-
async def get_athlete_stats_with_token(
|
|
203
|
-
|
|
231
|
+
async def get_athlete_stats_with_token(
|
|
232
|
+
access_token: str,
|
|
233
|
+
after: int | None = None,
|
|
234
|
+
before: int | None = None,
|
|
235
|
+
page: int | None = None,
|
|
236
|
+
per_page: int | None = None
|
|
237
|
+
) -> dict:
|
|
238
|
+
"""Get athlete activities using an existing access token with optional filters.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
access_token: Strava access token
|
|
242
|
+
after: An epoch timestamp to use for filtering activities that have taken place after a certain time
|
|
243
|
+
before: An epoch timestamp to use for filtering activities that have taken place before a certain time
|
|
244
|
+
page: The page of activities (default=1)
|
|
245
|
+
per_page: How many activities per page (default=30)
|
|
246
|
+
"""
|
|
204
247
|
if not access_token:
|
|
205
248
|
return {"error": "access token is required"}
|
|
206
249
|
|
|
207
|
-
|
|
250
|
+
# Build URL with query parameters
|
|
251
|
+
params = []
|
|
252
|
+
if after is not None:
|
|
253
|
+
params.append(f"after={after}")
|
|
254
|
+
if before is not None:
|
|
255
|
+
params.append(f"before={before}")
|
|
256
|
+
if page is not None:
|
|
257
|
+
params.append(f"page={page}")
|
|
258
|
+
if per_page is not None:
|
|
259
|
+
params.append(f"per_page={per_page}")
|
|
260
|
+
|
|
261
|
+
# Default per_page to 30 if not specified (Strava API default)
|
|
262
|
+
if per_page is None:
|
|
263
|
+
params.append("per_page=30")
|
|
264
|
+
|
|
265
|
+
query_string = "&".join(params) if params else ""
|
|
266
|
+
url = f"https://www.strava.com/api/v3/athlete/activities?{query_string}"
|
|
267
|
+
|
|
208
268
|
headers = {
|
|
209
269
|
"accept": "application/json",
|
|
210
270
|
"authorization": f"Bearer {access_token}"
|
|
@@ -219,7 +279,12 @@ async def get_athlete_stats_with_token(access_token: str) -> dict:
|
|
|
219
279
|
except Exception as e:
|
|
220
280
|
return {"error": "API request failed", "status_code": response.status_code, "response": response.text, "error": str(e)}
|
|
221
281
|
|
|
222
|
-
|
|
282
|
+
activities_data = response.json()
|
|
283
|
+
return {
|
|
284
|
+
activities_data,
|
|
285
|
+
"count": len(activities_data) if isinstance(activities_data, list) else 0,
|
|
286
|
+
"status": "success"
|
|
287
|
+
}
|
|
223
288
|
|
|
224
289
|
@mcp.tool("strava://auth/save")
|
|
225
290
|
async def save_tokens(tokens: dict | None = None) -> dict:
|
|
@@ -241,8 +306,24 @@ async def load_tokens() -> dict:
|
|
|
241
306
|
return {"ok": True, "tokens": result.get("tokens"), "path": result.get("path")}
|
|
242
307
|
|
|
243
308
|
@mcp.tool("strava://athlete/refresh-and-stats")
|
|
244
|
-
async def refresh_and_get_stats(
|
|
245
|
-
|
|
309
|
+
async def refresh_and_get_stats(
|
|
310
|
+
client_id: int | None = None,
|
|
311
|
+
client_secret: str | None = None,
|
|
312
|
+
after: int | None = None,
|
|
313
|
+
before: int | None = None,
|
|
314
|
+
page: int | None = None,
|
|
315
|
+
per_page: int | None = None
|
|
316
|
+
) -> dict:
|
|
317
|
+
"""Load saved refresh token, refresh access token, save it, then fetch activities with optional filters.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
client_id: Strava client ID
|
|
321
|
+
client_secret: Strava client secret
|
|
322
|
+
after: An epoch timestamp to use for filtering activities that have taken place after a certain time
|
|
323
|
+
before: An epoch timestamp to use for filtering activities that have taken place before a certain time
|
|
324
|
+
page: The page of activities (default=1)
|
|
325
|
+
per_page: How many activities per page (default=30)
|
|
326
|
+
"""
|
|
246
327
|
load_result = await load_tokens()
|
|
247
328
|
if not load_result.get("ok"):
|
|
248
329
|
return {"error": "no saved tokens", "details": load_result}
|
|
@@ -262,9 +343,15 @@ async def refresh_and_get_stats(client_id: int | None = None, client_secret: str
|
|
|
262
343
|
if not access_token:
|
|
263
344
|
return {"error": "no access_token after refresh"}
|
|
264
345
|
|
|
265
|
-
# Fetch activities with new token
|
|
266
|
-
activities = await get_athlete_stats_with_token(
|
|
267
|
-
|
|
346
|
+
# Fetch activities with new token and filters
|
|
347
|
+
activities = await get_athlete_stats_with_token(
|
|
348
|
+
access_token=access_token,
|
|
349
|
+
after=after,
|
|
350
|
+
before=before,
|
|
351
|
+
page=page,
|
|
352
|
+
per_page=per_page
|
|
353
|
+
)
|
|
354
|
+
return {activities, "tokens": refreshed}
|
|
268
355
|
|
|
269
356
|
@mcp.tool("strava://session/start")
|
|
270
357
|
async def start_session(client_id: int | None = None, client_secret: str | None = None) -> dict:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
version = 1
|
|
2
|
-
revision =
|
|
2
|
+
revision = 2
|
|
3
3
|
requires-python = ">=3.10"
|
|
4
4
|
|
|
5
5
|
[[package]]
|
|
@@ -1031,7 +1031,7 @@ wheels = [
|
|
|
1031
1031
|
|
|
1032
1032
|
[[package]]
|
|
1033
1033
|
name = "strava-activity-mcp-server"
|
|
1034
|
-
version = "0.2.
|
|
1034
|
+
version = "0.2.5"
|
|
1035
1035
|
source = { editable = "." }
|
|
1036
1036
|
dependencies = [
|
|
1037
1037
|
{ name = "build" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strava_activity_mcp_server-0.2.3 → strava_activity_mcp_server-0.2.5}/ref/mcp_pypi_example.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|