note-connector 0.2.7 → 0.2.8

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "note-connector",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/py/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "note-connector"
3
- version = "0.2.7"
3
+ version = "0.2.8"
4
4
  description = "note-connector: MCP server and ChatGPT connector for note.com"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -117,6 +117,43 @@ _MAGIC_BYTES: dict[str, tuple[bytes, int]] = {
117
117
  }
118
118
 
119
119
 
120
+ def _extract_image_url_from_response(response: dict[str, Any]) -> str | None:
121
+ """Extract image URL from API response, trying multiple possible field names.
122
+
123
+ note.com API may return the URL under different field names.
124
+ This function tries known field names in order of preference.
125
+
126
+ Args:
127
+ response: Full API response dictionary
128
+
129
+ Returns:
130
+ Image URL string if found, None otherwise
131
+ """
132
+ data = response.get("data", {})
133
+
134
+ # Try known field names for image URL
135
+ candidate_keys = [
136
+ "url",
137
+ "image_url",
138
+ "src",
139
+ "download_url",
140
+ "note_image_url",
141
+ ]
142
+
143
+ for key in candidate_keys:
144
+ value = data.get(key)
145
+ if value and isinstance(value, str) and value.strip():
146
+ return str(value)
147
+
148
+ # Check if the top-level response itself is a string URL
149
+ for key in candidate_keys:
150
+ value = response.get(key)
151
+ if value and isinstance(value, str) and value.strip():
152
+ return str(value)
153
+
154
+ return None
155
+
156
+
120
157
  def validate_image_file(file_path: str) -> None:
121
158
  """Validate image file before upload.
122
159
 
@@ -207,25 +244,36 @@ async def _upload_image_internal(
207
244
  async with NoteAPIClient(session) as client:
208
245
  response = await client.post(endpoint, files=files, data=data)
209
246
 
210
- # Parse response - Article 6: validate required fields, no fallback
211
- image_data = response.get("data", {})
247
+ # Debug: log full API response for investigation
248
+ logger.debug(
249
+ "Image upload response for note_id=%s, endpoint=%s: %s",
250
+ numeric_note_id,
251
+ endpoint,
252
+ {k: v for k, v in response.items() if k != "data"},
253
+ )
254
+ if "data" in response:
255
+ logger.debug("Image upload response data keys: %s", list(response["data"].keys()))
256
+ logger.debug("Image upload response data: %s", response["data"])
212
257
 
213
- # Note: The eyecatch upload endpoint (/v1/image_upload/note_eyecatch) returns
214
- # only 'url' in the response, not 'key'. This is expected behavior based on
215
- # API testing - body images (via presigned_post) return 'key', eyecatch does not.
258
+ # Extract image URL from response (tries multiple field names)
259
+ image_url = _extract_image_url_from_response(response)
260
+
261
+ image_data = response.get("data", {})
216
262
  image_key = image_data.get("key")
217
263
 
218
- image_url = image_data.get("url")
264
+ # URL is optional - eyecatch API may not always return it
265
+ # The eyecatch is set server-side even without a URL in the response
219
266
  if not image_url:
220
- raise NoteAPIError(
221
- code=ErrorCode.API_ERROR,
222
- message="Image upload failed: API response missing required field 'url'",
223
- details={"response": response},
267
+ logger.warning(
268
+ "Image upload response missing image URL for note_id=%s. Response keys: top=%s, data=%s",
269
+ numeric_note_id,
270
+ list(response.keys()),
271
+ list(image_data.keys()) if image_data else "none",
224
272
  )
225
273
 
226
274
  return Image(
227
275
  key=str(image_key) if image_key else None,
228
- url=str(image_url),
276
+ url=str(image_url) if image_url else "",
229
277
  original_path=file_path,
230
278
  size_bytes=file_size,
231
279
  uploaded_at=int(time.time()),
@@ -285,7 +285,9 @@ async def note_upload_eyecatch(
285
285
  アップロード結果(画像URLを含む)
286
286
  """
287
287
  image = await upload_eyecatch_image(session, file_path, note_id=note_id)
288
- return f"アイキャッチ画像をアップロードしました。URL: {image.url}"
288
+ if image.url:
289
+ return f"アイキャッチ画像をアップロードしました。URL: {image.url}"
290
+ return "アイキャッチ画像をアップロードしました。"
289
291
 
290
292
 
291
293
  @mcp.tool()
@@ -319,7 +321,9 @@ async def note_set_eyecatch_base64(
319
321
  mime_type=mime_type,
320
322
  image_base64=image_base64,
321
323
  )
322
- return f"アイキャッチ画像を設定しました。URL: {image.url}"
324
+ if image.url:
325
+ return f"アイキャッチ画像を設定しました。URL: {image.url}"
326
+ return "アイキャッチ画像を設定しました。"
323
327
 
324
328
 
325
329
  @mcp.tool()