mathpix 0.1.0

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +52 -0
  3. data/LICENSE +21 -0
  4. data/README.md +171 -0
  5. data/SECURITY.md +137 -0
  6. data/lib/mathpix/balanced_ternary.rb +86 -0
  7. data/lib/mathpix/batch.rb +155 -0
  8. data/lib/mathpix/capture_builder.rb +142 -0
  9. data/lib/mathpix/chemistry.rb +69 -0
  10. data/lib/mathpix/client.rb +439 -0
  11. data/lib/mathpix/configuration.rb +187 -0
  12. data/lib/mathpix/configuration.rb.backup +125 -0
  13. data/lib/mathpix/conversion.rb +257 -0
  14. data/lib/mathpix/document.rb +320 -0
  15. data/lib/mathpix/errors.rb +78 -0
  16. data/lib/mathpix/mcp/auth/oauth_provider.rb +346 -0
  17. data/lib/mathpix/mcp/auth/token_manager.rb +31 -0
  18. data/lib/mathpix/mcp/auth.rb +18 -0
  19. data/lib/mathpix/mcp/base_tool.rb +117 -0
  20. data/lib/mathpix/mcp/elicitations/ambiguity_elicitation.rb +162 -0
  21. data/lib/mathpix/mcp/elicitations/base_elicitation.rb +141 -0
  22. data/lib/mathpix/mcp/elicitations/confidence_elicitation.rb +162 -0
  23. data/lib/mathpix/mcp/elicitations.rb +78 -0
  24. data/lib/mathpix/mcp/middleware/cors_middleware.rb +94 -0
  25. data/lib/mathpix/mcp/middleware/oauth_middleware.rb +72 -0
  26. data/lib/mathpix/mcp/middleware/rate_limiting_middleware.rb +140 -0
  27. data/lib/mathpix/mcp/middleware.rb +13 -0
  28. data/lib/mathpix/mcp/resources/formats_list_resource.rb +113 -0
  29. data/lib/mathpix/mcp/resources/hierarchical_router.rb +237 -0
  30. data/lib/mathpix/mcp/resources/latest_snip_resource.rb +60 -0
  31. data/lib/mathpix/mcp/resources/recent_snips_resource.rb +75 -0
  32. data/lib/mathpix/mcp/resources/snip_stats_resource.rb +78 -0
  33. data/lib/mathpix/mcp/resources.rb +15 -0
  34. data/lib/mathpix/mcp/server.rb +174 -0
  35. data/lib/mathpix/mcp/tools/batch_convert_tool.rb +106 -0
  36. data/lib/mathpix/mcp/tools/check_document_status_tool.rb +66 -0
  37. data/lib/mathpix/mcp/tools/convert_document_tool.rb +90 -0
  38. data/lib/mathpix/mcp/tools/convert_image_tool.rb +91 -0
  39. data/lib/mathpix/mcp/tools/convert_strokes_tool.rb +82 -0
  40. data/lib/mathpix/mcp/tools/get_account_info_tool.rb +57 -0
  41. data/lib/mathpix/mcp/tools/get_usage_tool.rb +62 -0
  42. data/lib/mathpix/mcp/tools/list_formats_tool.rb +81 -0
  43. data/lib/mathpix/mcp/tools/search_results_tool.rb +111 -0
  44. data/lib/mathpix/mcp/transports/http_streaming_transport.rb +622 -0
  45. data/lib/mathpix/mcp/transports/sse_stream_handler.rb +236 -0
  46. data/lib/mathpix/mcp/transports.rb +12 -0
  47. data/lib/mathpix/mcp.rb +52 -0
  48. data/lib/mathpix/result.rb +364 -0
  49. data/lib/mathpix/version.rb +22 -0
  50. data/lib/mathpix.rb +229 -0
  51. metadata +283 -0
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base_tool'
4
+
5
+ module Mathpix
6
+ module MCP
7
+ module Tools
8
+ # Get Usage Tool
9
+ #
10
+ # Retrieves API usage statistics and account limits
11
+ # Thin delegate to Mathpix::Client (usage endpoint)
12
+ #
13
+ # The geodesic path: official SDK + usage tracking
14
+ class GetUsageTool < BaseTool
15
+ description "Get Mathpix API usage statistics and remaining credits"
16
+
17
+ input_schema(
18
+ properties: {
19
+ detailed: {
20
+ type: "boolean",
21
+ description: "Include detailed breakdown by operation type (default: false)"
22
+ }
23
+ },
24
+ required: []
25
+ )
26
+
27
+ def self.call(detailed: false, server_context:)
28
+ safe_execute do
29
+ client = mathpix_client(server_context)
30
+
31
+ begin
32
+ # Try to call the usage endpoint
33
+ usage_data = client.get('/usage')
34
+
35
+ response_data = {
36
+ success: true,
37
+ usage: {
38
+ requests_this_month: usage_data['requests_this_month'],
39
+ requests_remaining: usage_data['requests_remaining'],
40
+ plan: usage_data['plan'],
41
+ reset_date: usage_data['reset_date']
42
+ }
43
+ }
44
+
45
+ if detailed && usage_data['breakdown']
46
+ response_data[:breakdown] = usage_data['breakdown']
47
+ end
48
+
49
+ json_response(response_data)
50
+ rescue Mathpix::APIError => e
51
+ # Handle API-specific errors properly
52
+ error_response(e)
53
+ rescue StandardError => e
54
+ # Handle unexpected errors
55
+ error_response(e)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base_tool'
4
+
5
+ module Mathpix
6
+ module MCP
7
+ module Tools
8
+ # List Formats Tool
9
+ #
10
+ # Lists all available output formats for Mathpix OCR
11
+ # Static data from API documentation
12
+ #
13
+ # The geodesic path: official SDK + format catalog
14
+ class ListFormatsTool < BaseTool
15
+ description "List all available output formats for Mathpix OCR operations"
16
+
17
+ input_schema(
18
+ properties: {
19
+ category: {
20
+ type: "string",
21
+ description: "Filter by category: image, document, or all (default: all)",
22
+ enum: ["image", "document", "all"]
23
+ }
24
+ },
25
+ required: []
26
+ )
27
+
28
+ def self.call(category: "all", server_context:)
29
+ safe_execute do
30
+ # Static format definitions
31
+ image_formats = [
32
+ { name: "latex_styled", description: "LaTeX with styling", type: "image" },
33
+ { name: "text", description: "Plain text", type: "image" },
34
+ { name: "latex_list", description: "Array of LaTeX expressions", type: "image" },
35
+ { name: "mathml", description: "MathML markup", type: "image" },
36
+ { name: "asciimath", description: "AsciiMath notation", type: "image" },
37
+ { name: "text_display", description: "Display-style text", type: "image" },
38
+ { name: "latex_simplified", description: "Simplified LaTeX", type: "image" },
39
+ { name: "data", description: "Full response data with metadata", type: "image" },
40
+ { name: "html", description: "HTML markup", type: "image" }
41
+ ]
42
+
43
+ document_formats = [
44
+ { name: "markdown", description: "Markdown format", type: "document" },
45
+ { name: "latex", description: "LaTeX document", type: "document" },
46
+ { name: "html", description: "HTML document", type: "document" },
47
+ { name: "docx", description: "Microsoft Word document", type: "document" },
48
+ { name: "tex.zip", description: "LaTeX with figures (zipped)", type: "document" }
49
+ ]
50
+
51
+ # Filter by category
52
+ formats = case category
53
+ when "image"
54
+ image_formats
55
+ when "document"
56
+ document_formats
57
+ when "all"
58
+ image_formats + document_formats
59
+ else
60
+ image_formats + document_formats
61
+ end
62
+
63
+ # Format response
64
+ response_data = {
65
+ success: true,
66
+ category: category,
67
+ count: formats.length,
68
+ formats: formats,
69
+ usage: {
70
+ image_capture: "Use with snap() or ConvertImageTool",
71
+ document_conversion: "Use with document() or ConvertDocumentTool"
72
+ }
73
+ }
74
+
75
+ json_response(response_data)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base_tool'
4
+
5
+ module Mathpix
6
+ module MCP
7
+ module Tools
8
+ # Search Results Tool
9
+ #
10
+ # Searches recent capture results with optional filtering
11
+ # Thin delegate to Mathpix::Client#recent with search
12
+ #
13
+ # The geodesic path: official SDK + search filtering
14
+ class SearchResultsTool < BaseTool
15
+ description "Search recent Mathpix capture results with optional text filtering"
16
+
17
+ input_schema(
18
+ properties: {
19
+ query: {
20
+ type: "string",
21
+ description: "Search query to filter results by LaTeX or text content"
22
+ },
23
+ limit: {
24
+ type: "number",
25
+ description: "Maximum number of results to return (default: 10, max: 100)"
26
+ },
27
+ offset: {
28
+ type: "number",
29
+ description: "Offset for pagination (default: 0)"
30
+ },
31
+ include_content: {
32
+ type: "boolean",
33
+ description: "Include full LaTeX/text content in results (default: false)"
34
+ }
35
+ },
36
+ required: []
37
+ )
38
+
39
+ def self.call(query: nil, limit: 10, offset: 0, include_content: false, server_context:)
40
+ safe_execute do
41
+ client = mathpix_client(server_context)
42
+
43
+ # Validate and constrain limit
44
+ limit = [[limit.to_i, 1].max, 100].min
45
+
46
+ # Get recent results from API
47
+ recent_results = client.recent(limit: limit + offset)
48
+
49
+ # Apply offset
50
+ results = recent_results.drop(offset)
51
+
52
+ # Apply search filter if query provided
53
+ if query && !query.empty?
54
+ query_lower = query.downcase
55
+ results = results.select do |result|
56
+ (result.latex&.downcase&.include?(query_lower)) ||
57
+ (result.text&.downcase&.include?(query_lower))
58
+ end
59
+ end
60
+
61
+ # Limit after filtering
62
+ results = results.take(limit)
63
+
64
+ # Format results
65
+ formatted_results = results.map do |result|
66
+ item = {
67
+ id: result.request_id,
68
+ created_at: result.timestamp,
69
+ confidence: result.confidence,
70
+ is_printed: result.printed?,
71
+ is_handwritten: result.handwritten?
72
+ }
73
+
74
+ # Include content if requested
75
+ if include_content
76
+ item[:latex] = result.latex
77
+ item[:text] = result.text
78
+ else
79
+ # Just include preview
80
+ item[:latex_preview] = truncate(result.latex, 100)
81
+ item[:text_preview] = truncate(result.text, 100)
82
+ end
83
+
84
+ item
85
+ end
86
+
87
+ # Format response
88
+ response_data = {
89
+ success: true,
90
+ query: query,
91
+ limit: limit,
92
+ offset: offset,
93
+ count: formatted_results.length,
94
+ results: formatted_results
95
+ }
96
+
97
+ json_response(response_data)
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def self.truncate(text, max_length)
104
+ return nil unless text
105
+ return text if text.length <= max_length
106
+ "#{text[0...max_length]}..."
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end