scitex 2.15.2__py3-none-any.whl → 2.15.4__py3-none-any.whl

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.
Files changed (77) hide show
  1. scitex/_mcp_resources/__init__.py +2 -0
  2. scitex/_mcp_resources/_scholar.py +148 -0
  3. scitex/_mcp_tools/scholar.py +50 -99
  4. scitex/_mcp_tools/social.py +15 -232
  5. scitex/_mcp_tools/writer.py +7 -17
  6. scitex/canvas/mcp_server.py +16 -3
  7. scitex/capture/mcp_server.py +16 -2
  8. scitex/cli/audio.py +90 -20
  9. scitex/cli/capture.py +120 -0
  10. scitex/cli/introspect.py +19 -12
  11. scitex/cli/plt.py +78 -21
  12. scitex/cli/scholar/__init__.py +160 -2
  13. scitex/cli/scholar/_crossref_scitex.py +25 -266
  14. scitex/cli/scholar/_openalex_scitex.py +55 -0
  15. scitex/cli/social.py +63 -22
  16. scitex/cli/stats.py +121 -3
  17. scitex/cli/writer.py +49 -423
  18. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +90 -0
  19. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +1571 -0
  20. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +6262 -0
  21. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +1274 -0
  22. scitex/dev/plt/data/mpl/dir_ax.txt +459 -0
  23. scitex/introspect/_list_api.py +5 -2
  24. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  25. scitex/scholar/__init__.py +14 -9
  26. scitex/scholar/_mcp/crossref_tool_schemas.py +133 -0
  27. scitex/scholar/_mcp/openalex_handlers.py +212 -0
  28. scitex/scholar/_mcp/openalex_tool_schemas.py +96 -0
  29. scitex/scholar/_mcp/tool_schemas.py +16 -1
  30. scitex/scholar/data/.gitkeep +0 -0
  31. scitex/scholar/data/README.md +44 -0
  32. scitex/scholar/data/bib_files/bibliography.bib +1952 -0
  33. scitex/scholar/data/bib_files/neurovista.bib +277 -0
  34. scitex/scholar/data/bib_files/neurovista_enriched.bib +441 -0
  35. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +441 -0
  36. scitex/scholar/data/bib_files/neurovista_processed.bib +338 -0
  37. scitex/scholar/data/bib_files/openaccess.bib +89 -0
  38. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +2178 -0
  39. scitex/scholar/data/bib_files/pac.bib +698 -0
  40. scitex/scholar/data/bib_files/pac_enriched.bib +1061 -0
  41. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  42. scitex/scholar/data/bib_files/pac_titles.txt +75 -0
  43. scitex/scholar/data/bib_files/paywalled.bib +98 -0
  44. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +58 -0
  45. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +87 -0
  46. scitex/scholar/data/bib_files/seizure_prediction.bib +694 -0
  47. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  48. scitex/scholar/data/bib_files/test_complete_enriched.bib +437 -0
  49. scitex/scholar/data/bib_files/test_final_enriched.bib +437 -0
  50. scitex/scholar/data/bib_files/test_seizure.bib +46 -0
  51. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  52. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  53. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  54. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  55. scitex/scholar/data/impact_factor.db +0 -0
  56. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  57. scitex/scholar/local_dbs/__init__.py +31 -0
  58. scitex/scholar/local_dbs/crossref_scitex.py +30 -0
  59. scitex/scholar/local_dbs/openalex_scitex.py +30 -0
  60. scitex/scholar/mcp_server.py +59 -4
  61. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  62. scitex/stats/mcp_server.py +16 -3
  63. scitex/template/mcp_server.py +16 -3
  64. scitex/ui/mcp_server.py +16 -3
  65. scitex/writer/__init__.py +43 -34
  66. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/METADATA +22 -3
  67. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/RECORD +70 -38
  68. scitex/scholar/crossref_scitex.py +0 -367
  69. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +0 -462
  70. scitex/scholar/url_finder/.tmp/open_url/README.md +0 -223
  71. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +0 -694
  72. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +0 -1160
  73. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +0 -344
  74. scitex/scholar/url_finder/.tmp/open_url/__init__.py +0 -24
  75. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/WHEEL +0 -0
  76. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/entry_points.txt +0 -0
  77. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,244 +1,27 @@
1
1
  #!/usr/bin/env python3
2
- # Timestamp: 2026-01-22
3
- # File: /home/ywatanabe/proj/scitex-code/src/scitex/_mcp_tools/social.py
2
+ # Timestamp: 2026-01-27
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/_mcp_tools/social.py
4
+ """Social module tools - thin wrapper delegating to socialia package.
4
5
 
5
- """Social media module tools for FastMCP unified server.
6
-
7
- All MCP tools delegate to socialia CLI for reproducibility.
8
- Each tool returns the CLI command used, enabling human reproduction.
6
+ Single source of truth: socialia MCP tools.
9
7
  """
10
8
 
11
9
  from __future__ import annotations
12
10
 
13
- import json
14
- import subprocess
15
- import sys
16
- from typing import Optional
17
-
18
- # Import platform strategies from socialia for MCP tool descriptions
19
- try:
20
- from scitex.social import PLATFORM_STRATEGIES
21
- except ImportError:
22
- try:
23
- from socialia import PLATFORM_STRATEGIES
24
- except ImportError:
25
- PLATFORM_STRATEGIES = """
26
- ## Platform Strategies (install socialia for full guide)
27
- - twitter: 280 chars, hook first, 1-2 hashtags at end
28
- - linkedin: 3000 chars, first 2 lines critical, 3-5 hashtags at end
29
- - reddit: title is key, no hashtags, value first
30
- - youtube: keyword-rich title <60 chars, 3-5 hashtags in description
31
- """
32
-
33
-
34
- def _json(data: dict) -> str:
35
- return json.dumps(data, indent=2, default=str)
36
-
37
-
38
- def _run_socialia_cli(*args: str) -> dict:
39
- """Run socialia CLI and return structured result."""
40
- cmd = [sys.executable, "-m", "socialia", "--json", *args]
41
- cli_command = f"socialia {' '.join(args)}"
42
-
43
- try:
44
- result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
45
-
46
- if result.returncode == 0:
47
- try:
48
- data = json.loads(result.stdout)
49
- data["cli_command"] = cli_command
50
- return data
51
- except json.JSONDecodeError:
52
- return {
53
- "success": True,
54
- "output": result.stdout,
55
- "cli_command": cli_command,
56
- }
57
- else:
58
- return {
59
- "success": False,
60
- "error": result.stderr or result.stdout,
61
- "cli_command": cli_command,
62
- }
63
- except subprocess.TimeoutExpired:
64
- return {
65
- "success": False,
66
- "error": "Command timed out",
67
- "cli_command": cli_command,
68
- }
69
- except Exception as e:
70
- return {
71
- "success": False,
72
- "error": str(e),
73
- "cli_command": cli_command,
74
- }
75
-
76
11
 
77
12
  def register_social_tools(mcp) -> None:
78
- """Register social media tools with FastMCP server."""
79
-
80
- @mcp.tool()
81
- async def social_post(
82
- platform: str,
83
- text: str,
84
- reply_to: Optional[str] = None,
85
- quote: Optional[str] = None,
86
- subreddit: Optional[str] = None,
87
- title: Optional[str] = None,
88
- dry_run: bool = False,
89
- ) -> str:
90
- """[social] Post content to a social media platform (twitter, linkedin, reddit, youtube).
91
-
92
- PLATFORM STRATEGIES:
93
- - twitter: 280 chars. Hook first. 1+ emoji. GitHub URL. 3-5 hashtags at END for SEO
94
- - linkedin: 3000 chars. First 2 lines critical. Short paragraphs. Include project links. 3-5 hashtags at END
95
- - reddit: Title is key. NO hashtags. Value first, self-promo last. Link in body
96
- - youtube: Keyword-rich title <60 chars. Links in description. 3-5 hashtags in description
97
-
98
- BAD: "SciTeX v2.15.0 released! New feature. #Python #AI"
99
- GOOD: "Your AI agent can now speak to you remotely.
100
-
101
- Audio relay bridges the gap.
102
-
103
- github.com/user/repo
104
-
105
- #AIAgents #Python"
106
- """
107
- args = ["post", platform, text]
108
-
109
- if reply_to:
110
- args.extend(["--reply-to", reply_to])
111
- if quote:
112
- args.extend(["--quote", quote])
113
- if subreddit and platform == "reddit":
114
- args.extend(["--subreddit", subreddit])
115
- if title:
116
- args.extend(["--title", title])
117
- if dry_run:
118
- args.append("--dry-run")
119
-
120
- result = _run_socialia_cli(*args)
121
- return _json(result)
122
-
123
- @mcp.tool()
124
- async def social_delete(
125
- platform: str,
126
- post_id: str,
127
- ) -> str:
128
- """[social] Delete a post from a platform (twitter, linkedin, reddit)."""
129
- result = _run_socialia_cli("delete", platform, post_id)
130
- return _json(result)
131
-
132
- @mcp.tool()
133
- async def social_status() -> str:
134
- """[social] Check social media configuration and authentication status."""
135
- result = _run_socialia_cli("status")
136
- return _json(result)
137
-
138
- @mcp.tool()
139
- async def social_analytics(
140
- platform: str,
141
- days: int = 7,
142
- ) -> str:
143
- """[social] Get analytics for a platform (twitter, youtube, ga)."""
144
- result = _run_socialia_cli("analytics", platform, "--days", str(days))
145
- return _json(result)
146
-
147
- @mcp.tool()
148
- async def social_thread(
149
- platform: str,
150
- posts: list[str],
151
- delay: int = 2,
152
- dry_run: bool = False,
153
- ) -> str:
154
- """[social] Post a thread of connected posts. Posts are list of strings."""
155
- import tempfile
156
- from pathlib import Path
157
-
158
- # Write posts to temp file (socialia expects file input)
159
- thread_content = "\n---\n".join(posts)
160
- with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
161
- f.write(thread_content)
162
- temp_path = f.name
163
-
164
- try:
165
- args = ["thread", platform, "--file", temp_path, "--delay", str(delay)]
166
- if dry_run:
167
- args.append("--dry-run")
168
- result = _run_socialia_cli(*args)
169
- result["thread_posts"] = posts
170
- return _json(result)
171
- finally:
172
- Path(temp_path).unlink(missing_ok=True)
173
-
174
- @mcp.tool()
175
- async def social_check_availability() -> str:
176
- """[social] Check if socialia is installed and list available platforms."""
177
- try:
178
- from scitex.social import (
179
- SOCIALIA_AVAILABLE,
180
- __socialia_version__,
181
- )
182
-
183
- if SOCIALIA_AVAILABLE:
184
- return _json(
185
- {
186
- "available": True,
187
- "version": __socialia_version__,
188
- "platforms": ["twitter", "linkedin", "reddit", "youtube"],
189
- "analytics": ["twitter", "youtube", "ga"],
190
- }
191
- )
192
- else:
193
- return _json(
194
- {
195
- "available": False,
196
- "error": "socialia not installed",
197
- "install_command": "pip install socialia",
198
- }
199
- )
200
- except Exception as e:
201
- return _json(
202
- {
203
- "available": False,
204
- "error": str(e),
205
- }
206
- )
207
-
208
- @mcp.tool()
209
- async def social_check(
210
- platform: Optional[str] = None,
211
- ) -> str:
212
- """[social] Check platform connection status (v0.1.4+)."""
213
- args = ["check"]
214
- if platform:
215
- args.append(platform)
216
- result = _run_socialia_cli(*args)
217
- return _json(result)
218
-
219
- @mcp.tool()
220
- async def social_feed(
221
- platform: Optional[str] = None,
222
- limit: int = 10,
223
- mentions: bool = False,
224
- ) -> str:
225
- """[social] Get recent posts from platform feeds (v0.1.4+)."""
226
- args = ["feed"]
227
- if platform:
228
- args.append(platform)
229
- args.extend(["--limit", str(limit)])
230
- if mentions:
231
- args.append("--mentions")
232
- result = _run_socialia_cli(*args)
233
- return _json(result)
13
+ """Register social tools by delegating to socialia package."""
14
+ try:
15
+ from socialia._mcp.tools import register_all_tools
234
16
 
235
- @mcp.tool()
236
- async def social_me(
237
- platform: str,
238
- ) -> str:
239
- """[social] Get user profile information (v0.1.4+)."""
240
- result = _run_socialia_cli("me", platform)
241
- return _json(result)
17
+ # Delegate all MCP tools to socialia
18
+ register_all_tools(mcp)
19
+ except ImportError:
20
+ # Fallback when socialia is not installed
21
+ @mcp.tool()
22
+ def social_status() -> str:
23
+ """[social] Get social media status (not installed)."""
24
+ return "socialia is required. Install with: pip install socialia"
242
25
 
243
26
 
244
27
  # EOF
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env python3
2
- # Timestamp: 2026-01-20
2
+ # Timestamp: 2026-01-27
3
3
  # File: /home/ywatanabe/proj/scitex-code/src/scitex/_mcp_tools/writer.py
4
- """Writer module tools - delegates to scitex-writer package.
4
+ """Writer module tools - thin wrapper delegating to scitex-writer package.
5
5
 
6
- Provides usage instructions for shell-based compilation workflow.
6
+ Single source of truth: scitex-writer MCP tools.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -12,26 +12,16 @@ from __future__ import annotations
12
12
  def register_writer_tools(mcp) -> None:
13
13
  """Register writer tools by delegating to scitex-writer package."""
14
14
  try:
15
- from scitex_writer._server import INSTRUCTIONS
15
+ from scitex_writer._mcp.tools import register_all_tools
16
16
 
17
- _SCITEX_WRITER_AVAILABLE = True
17
+ # Delegate all MCP tools to scitex-writer
18
+ register_all_tools(mcp)
18
19
  except ImportError:
19
- _SCITEX_WRITER_AVAILABLE = False
20
- INSTRUCTIONS = None
21
-
22
- if not _SCITEX_WRITER_AVAILABLE:
23
-
20
+ # Fallback when scitex-writer is not installed
24
21
  @mcp.tool()
25
22
  def writer_usage() -> str:
26
23
  """[writer] Get usage guide for SciTeX Writer (not installed)."""
27
24
  return "scitex-writer is required. Install with: pip install scitex-writer"
28
25
 
29
- return
30
-
31
- @mcp.tool()
32
- def writer_usage() -> str:
33
- """[writer] Get usage guide for SciTeX Writer LaTeX manuscript compilation system."""
34
- return INSTRUCTIONS
35
-
36
26
 
37
27
  # EOF
@@ -3,8 +3,12 @@
3
3
  # File: src/scitex/canvas/mcp_server.py
4
4
  # ----------------------------------------
5
5
 
6
- """
7
- MCP Server for SciTeX canvas - Multi-panel figure composition.
6
+ """MCP Server for SciTeX canvas - Multi-panel figure composition.
7
+
8
+ .. deprecated::
9
+ This standalone server is deprecated. Use the unified scitex MCP server:
10
+ CLI: scitex serve
11
+ Python: from scitex.mcp_server import run_server
8
12
 
9
13
  Provides tools for:
10
14
  - Creating canvas workspaces
@@ -15,6 +19,15 @@ Provides tools for:
15
19
 
16
20
  from __future__ import annotations
17
21
 
22
+ import warnings
23
+
24
+ warnings.warn(
25
+ "scitex.canvas.mcp_server is deprecated. Use 'scitex serve' or "
26
+ "'from scitex.mcp_server import run_server' for the unified MCP server.",
27
+ DeprecationWarning,
28
+ stacklevel=2,
29
+ )
30
+
18
31
  import asyncio
19
32
 
20
33
  # Graceful MCP dependency handling
@@ -126,7 +139,7 @@ async def _run_server():
126
139
 
127
140
 
128
141
  def main():
129
- """Main entry point for the MCP server."""
142
+ """Run the MCP server."""
130
143
  if not MCP_AVAILABLE:
131
144
  import sys
132
145
 
@@ -10,11 +10,25 @@ __FILE__ = "./src/scitex/capture/mcp_server.py"
10
10
  __DIR__ = os.path.dirname(__FILE__)
11
11
  # ----------------------------------------
12
12
 
13
- """
14
- MCP Server for SciTeX Capture - Screen Capture for Python
13
+ """MCP Server for SciTeX Capture - Screen Capture for Python.
14
+
15
+ .. deprecated::
16
+ This standalone server is deprecated. Use the unified scitex MCP server:
17
+ CLI: scitex serve
18
+ Python: from scitex.mcp_server import run_server
19
+
15
20
  Provides screenshot capture capabilities via Model Context Protocol.
16
21
  """
17
22
 
23
+ import warnings
24
+
25
+ warnings.warn(
26
+ "scitex.capture.mcp_server is deprecated. Use 'scitex serve' or "
27
+ "'from scitex.mcp_server import run_server' for the unified MCP server.",
28
+ DeprecationWarning,
29
+ stacklevel=2,
30
+ )
31
+
18
32
  import asyncio
19
33
  import base64
20
34
  from datetime import datetime
scitex/cli/audio.py CHANGED
@@ -240,7 +240,28 @@ def stop():
240
240
  sys.exit(1)
241
241
 
242
242
 
243
- @audio.command()
243
+ @audio.group(invoke_without_command=True)
244
+ @click.pass_context
245
+ def mcp(ctx):
246
+ """
247
+ MCP (Model Context Protocol) server operations
248
+
249
+ \b
250
+ Commands:
251
+ start - Start the MCP server
252
+ doctor - Check MCP server health
253
+ list-tools - List available MCP tools
254
+
255
+ \b
256
+ Examples:
257
+ scitex audio mcp start
258
+ scitex audio mcp start -t http --port 31293
259
+ """
260
+ if ctx.invoked_subcommand is None:
261
+ click.echo(ctx.get_help())
262
+
263
+
264
+ @mcp.command()
244
265
  @click.option(
245
266
  "-t",
246
267
  "--transport",
@@ -259,9 +280,9 @@ def stop():
259
280
  type=int,
260
281
  help="Port for HTTP/SSE transport (default: 31293)",
261
282
  )
262
- def serve(transport, host, port):
283
+ def start(transport, host, port):
263
284
  """
264
- Run MCP server for remote audio playback
285
+ Start the MCP server for remote audio playback
265
286
 
266
287
  Enables remote agents (via SSH) to play audio on local speakers.
267
288
 
@@ -273,22 +294,9 @@ def serve(transport, host, port):
273
294
 
274
295
  \b
275
296
  Examples:
276
- # Local stdio (Claude Desktop)
277
- scitex audio serve
278
-
279
- # HTTP server for remote agents
280
- scitex audio serve -t http --port 31293
281
-
282
- # SSE server
283
- scitex audio serve -t sse --port 31293
284
-
285
- \b
286
- Remote Setup:
287
- 1. Local: scitex audio serve -t http --port 31293
288
- 2. SSH: Add to ~/.ssh/config:
289
- LocalForward 31293 127.0.0.1:31293
290
- 3. Remote MCP config:
291
- {"type": "sse", "url": "http://localhost:31293/sse"}
297
+ scitex audio mcp start
298
+ scitex audio mcp start -t http --port 31293
299
+ scitex audio mcp start -t sse --port 31293
292
300
  """
293
301
  try:
294
302
  from scitex.audio.mcp_server import FASTMCP_AVAILABLE, run_server
@@ -320,6 +328,68 @@ def serve(transport, host, port):
320
328
  sys.exit(1)
321
329
 
322
330
 
331
+ @mcp.command()
332
+ def doctor():
333
+ """
334
+ Check MCP server health and dependencies
335
+
336
+ \b
337
+ Example:
338
+ scitex audio mcp doctor
339
+ """
340
+ click.secho("Audio MCP Server Health Check", fg="cyan", bold=True)
341
+ click.echo()
342
+
343
+ # Check fastmcp
344
+ click.echo("Checking FastMCP... ", nl=False)
345
+ try:
346
+ from scitex.audio.mcp_server import FASTMCP_AVAILABLE
347
+
348
+ if FASTMCP_AVAILABLE:
349
+ click.secho("OK", fg="green")
350
+ else:
351
+ click.secho("NOT INSTALLED", fg="red")
352
+ click.echo(" Install with: pip install fastmcp")
353
+ except ImportError:
354
+ click.secho("FAIL", fg="red")
355
+
356
+ # Check audio backends
357
+ click.echo("Checking audio backends... ", nl=False)
358
+ try:
359
+ from scitex.audio import available_backends
360
+
361
+ backends = available_backends()
362
+ if backends:
363
+ click.secho(f"OK ({', '.join(backends)})", fg="green")
364
+ else:
365
+ click.secho("NONE AVAILABLE", fg="yellow")
366
+ except Exception as e:
367
+ click.secho(f"FAIL ({e})", fg="red")
368
+
369
+
370
+ @mcp.command("list-tools")
371
+ def list_tools():
372
+ """
373
+ List available MCP tools
374
+
375
+ \b
376
+ Example:
377
+ scitex audio mcp list-tools
378
+ """
379
+ click.secho("Audio MCP Tools", fg="cyan", bold=True)
380
+ click.echo()
381
+ tools = [
382
+ ("audio_speak", "Convert text to speech and play audio"),
383
+ ("audio_generate_audio", "Generate audio file without playing"),
384
+ ("audio_list_backends", "List available TTS backends"),
385
+ ("audio_list_voices", "List available voices for a backend"),
386
+ ("audio_play_audio", "Play an audio file"),
387
+ ("audio_check_audio_status", "Check audio system status"),
388
+ ]
389
+ for name, desc in tools:
390
+ click.echo(f" {name}: {desc}")
391
+
392
+
323
393
  @audio.command()
324
394
  @click.option(
325
395
  "--host",
@@ -359,7 +429,7 @@ def relay(host, port):
359
429
  try:
360
430
  from scitex.audio.mcp_server import run_relay_server
361
431
 
362
- click.secho(f"Starting audio relay server", fg="cyan")
432
+ click.secho("Starting audio relay server", fg="cyan")
363
433
  click.echo(f" Host: {host}")
364
434
  click.echo(f" Port: {port}")
365
435
  click.echo()
scitex/cli/capture.py CHANGED
@@ -314,5 +314,125 @@ def window(handle, output, quality):
314
314
  sys.exit(1)
315
315
 
316
316
 
317
+ @capture.group(invoke_without_command=True)
318
+ @click.pass_context
319
+ def mcp(ctx):
320
+ """
321
+ MCP (Model Context Protocol) server operations
322
+
323
+ \b
324
+ Commands:
325
+ start - Start the MCP server
326
+ doctor - Check MCP server health
327
+ list-tools - List available MCP tools
328
+
329
+ \b
330
+ Examples:
331
+ scitex capture mcp start
332
+ scitex capture mcp list-tools
333
+ """
334
+ if ctx.invoked_subcommand is None:
335
+ click.echo(ctx.get_help())
336
+
337
+
338
+ @mcp.command("start")
339
+ @click.option(
340
+ "-t",
341
+ "--transport",
342
+ type=click.Choice(["stdio", "sse", "http"]),
343
+ default="stdio",
344
+ help="Transport protocol (default: stdio)",
345
+ )
346
+ @click.option("--host", default="0.0.0.0", help="Host for HTTP/SSE (default: 0.0.0.0)")
347
+ @click.option(
348
+ "--port", default=8096, type=int, help="Port for HTTP/SSE (default: 8096)"
349
+ )
350
+ def mcp_start(transport, host, port):
351
+ """
352
+ Start the capture MCP server
353
+
354
+ \b
355
+ Examples:
356
+ scitex capture mcp start
357
+ scitex capture mcp start -t http --port 8096
358
+ """
359
+ try:
360
+ from scitex.capture.mcp_server import main as run_server
361
+
362
+ if transport != "stdio":
363
+ click.secho(f"Starting capture MCP server ({transport})", fg="cyan")
364
+ click.echo(f" Host: {host}")
365
+ click.echo(f" Port: {port}")
366
+
367
+ run_server()
368
+
369
+ except ImportError as e:
370
+ click.secho(f"Error: {e}", fg="red", err=True)
371
+ click.echo("\nInstall dependencies: pip install fastmcp")
372
+ sys.exit(1)
373
+ except Exception as e:
374
+ click.secho(f"Error: {e}", fg="red", err=True)
375
+ sys.exit(1)
376
+
377
+
378
+ @mcp.command()
379
+ def doctor():
380
+ """
381
+ Check MCP server health and dependencies
382
+
383
+ \b
384
+ Example:
385
+ scitex capture mcp doctor
386
+ """
387
+ click.secho("Capture MCP Server Health Check", fg="cyan", bold=True)
388
+ click.echo()
389
+
390
+ click.echo("Checking FastMCP... ", nl=False)
391
+ try:
392
+ import fastmcp # noqa: F401
393
+
394
+ click.secho("OK", fg="green")
395
+ except ImportError:
396
+ click.secho("NOT INSTALLED", fg="red")
397
+ click.echo(" Install with: pip install fastmcp")
398
+
399
+ click.echo("Checking capture module... ", nl=False)
400
+ try:
401
+ from scitex import capture as _ # noqa: F401
402
+
403
+ click.secho("OK", fg="green")
404
+ except ImportError as e:
405
+ click.secho(f"FAIL ({e})", fg="red")
406
+
407
+
408
+ @mcp.command("list-tools")
409
+ def list_tools():
410
+ """
411
+ List available MCP tools
412
+
413
+ \b
414
+ Example:
415
+ scitex capture mcp list-tools
416
+ """
417
+ click.secho("Capture MCP Tools", fg="cyan", bold=True)
418
+ click.echo()
419
+ tools = [
420
+ ("capture_capture_screenshot", "Capture screenshot"),
421
+ ("capture_capture_window", "Capture specific window"),
422
+ ("capture_start_monitoring", "Start continuous capture"),
423
+ ("capture_stop_monitoring", "Stop monitoring"),
424
+ ("capture_get_monitoring_status", "Get monitoring status"),
425
+ ("capture_analyze_screenshot", "Analyze screenshot for errors"),
426
+ ("capture_list_recent_screenshots", "List recent screenshots"),
427
+ ("capture_clear_cache", "Clear screenshot cache"),
428
+ ("capture_create_gif", "Create animated GIF"),
429
+ ("capture_list_sessions", "List monitoring sessions"),
430
+ ("capture_get_info", "Get monitor/window info"),
431
+ ("capture_list_windows", "List visible windows"),
432
+ ]
433
+ for name, desc in tools:
434
+ click.echo(f" {name}: {desc}")
435
+
436
+
317
437
  if __name__ == "__main__":
318
438
  capture()