mail_mcp 1.0.1 → 1.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c33a2c64482699085c980befb438655c2d599512422281bd3c3cb48a32582500
4
- data.tar.gz: 3dfbfeba54ef8f83d3bf99c7a727f7207c7245032c5df8eb6fbb53af4c089fb7
3
+ metadata.gz: e06ea684243f2124133903c64239d181b093c87e895f2716bb257d714f05aff3
4
+ data.tar.gz: b986c39811d9e1f3879b082796eba93ebb2a4a781c90309b2e1bb6e576875282
5
5
  SHA512:
6
- metadata.gz: ba9e0f6e1dd9b84146a637d748e2559d60080f5358fd83718d7e162a513d964d8f4e492335c96435bac2954176981553700eb89227c2e8a54d12e48c230dbc76
7
- data.tar.gz: 5063a62efb64854f0dfe47d2880621e9ee32a239280f78fd4d8202b405dbed45e8cc697a4269ceb59ba482e126c2cfcc6d2edbfbf29fd32abbf016ee4596d1fc
6
+ metadata.gz: 2ed02d68bf67c31a985199471ced7c4e9393485be999f6ed8fab254c19d55918cdc8e9d7aa3b7e35f631d0244ad3c5a38d22f21142681a613bc73aaa08b8e71d
7
+ data.tar.gz: 550a20589e633d0b29c7e1cf35ea0b48e2393452c2fbc07b2b51a6ae42805c2155952c8839aee0517bd054dea975dfb024468d0a759e06c3eb7961e9fa2ce396
data/lib/mail_mcp/app.rb CHANGED
@@ -37,7 +37,11 @@ module MailMCP
37
37
  tools: MCP_TOOLS,
38
38
  server_context: server_context
39
39
  )
40
- transport = MCP::Server::Transports::StreamableHTTPTransport.new(mcp_server, stateless: true)
40
+ transport = MCP::Server::Transports::StreamableHTTPTransport.new(
41
+ mcp_server,
42
+ stateless: true,
43
+ allowed_hosts: allowed_mcp_hosts
44
+ )
41
45
  status_code, resp_headers, body = transport.call(env)
42
46
  halt status_code, resp_headers, body
43
47
  end
@@ -144,7 +148,7 @@ module MailMCP
144
148
 
145
149
  def resolve_mcp_context
146
150
  auth = request.env["HTTP_AUTHORIZATION"]
147
- return [nil, nil] unless auth&.start_with?("Bearer ")
151
+ return [nil, unauthorized_response("missing bearer token")] unless auth&.start_with?("Bearer ")
148
152
 
149
153
  creds = JwtService.verify(auth[7..])
150
154
  context = CredentialContext.new(
@@ -161,12 +165,16 @@ module MailMCP
161
165
  )
162
166
  [context, nil]
163
167
  rescue JwtService::Error => e
164
- error = [
168
+ [nil, unauthorized_response(e.message)]
169
+ end
170
+
171
+ # 401 challenge that tells an MCP client where to run the OAuth flow.
172
+ def unauthorized_response(description)
173
+ [
165
174
  401,
166
175
  { "Content-Type" => "application/json", "WWW-Authenticate" => mcp_www_authenticate },
167
- [JSON.generate({ error: "invalid_token", error_description: e.message })]
176
+ [JSON.generate({ error: "invalid_token", error_description: description })]
168
177
  ]
169
- [nil, error]
170
178
  end
171
179
 
172
180
  def exchange_code(params, client_id)
@@ -216,6 +224,13 @@ module MailMCP
216
224
  ENV.fetch("BASE_URL")
217
225
  end
218
226
 
227
+ # The MCP transport's DNS-rebinding protection only accepts loopback `Host` values
228
+ # out of the box. This server is reached at its public hostname, so allow-list it —
229
+ # a bare host name matches any port.
230
+ def allowed_mcp_hosts
231
+ [URI.parse(base_url).host]
232
+ end
233
+
219
234
  def decode_client_id!(client_id)
220
235
  JwtService.decode_client_id(client_id.to_s)
221
236
  rescue JwtService::Error => e
@@ -190,12 +190,15 @@ module MailMCP
190
190
 
191
191
  def extract_attachments(mail)
192
192
  mail.attachments.map do |att|
193
+ # Mail#decoded does not memoize — it base64-decodes into a new String on
194
+ # every call, so decode once and reuse for both the upload and the size.
195
+ content = att.decoded
193
196
  url = AttachmentStore.upload(
194
- content: att.decoded,
197
+ content: content,
195
198
  filename: att.filename || "attachment",
196
199
  content_type: att.content_type
197
200
  )
198
- { filename: att.filename, content_type: att.content_type, size: att.decoded.bytesize, url: url }
201
+ { filename: att.filename, content_type: att.content_type, size: content.bytesize, url: url }
199
202
  end
200
203
  end
201
204
  end
@@ -1,3 +1,3 @@
1
1
  module MailMCP
2
- VERSION = "1.0.1".freeze
2
+ VERSION = "1.0.3".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edgars Beigarts
@@ -71,14 +71,14 @@ dependencies:
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '0.14'
74
+ version: 0.25.0
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '0.14'
81
+ version: 0.25.0
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: puma
84
84
  requirement: !ruby/object:Gem::Requirement