mathpix 0.1.1 → 0.1.2

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -0
  3. data/README.md +114 -1
  4. data/lib/mathpix/batch.rb +7 -8
  5. data/lib/mathpix/batched_document_conversion.rb +238 -0
  6. data/lib/mathpix/client.rb +33 -27
  7. data/lib/mathpix/configuration.rb +5 -9
  8. data/lib/mathpix/conversion.rb +2 -6
  9. data/lib/mathpix/document.rb +47 -12
  10. data/lib/mathpix/document_batcher.rb +191 -0
  11. data/lib/mathpix/mcp/auth/oauth_provider.rb +8 -9
  12. data/lib/mathpix/mcp/base_tool.rb +8 -5
  13. data/lib/mathpix/mcp/elicitations/ambiguity_elicitation.rb +8 -11
  14. data/lib/mathpix/mcp/elicitations/base_elicitation.rb +2 -0
  15. data/lib/mathpix/mcp/elicitations/confidence_elicitation.rb +2 -1
  16. data/lib/mathpix/mcp/elicitations.rb +1 -1
  17. data/lib/mathpix/mcp/middleware/cors_middleware.rb +2 -6
  18. data/lib/mathpix/mcp/middleware/oauth_middleware.rb +2 -6
  19. data/lib/mathpix/mcp/middleware/rate_limiting_middleware.rb +19 -18
  20. data/lib/mathpix/mcp/resources/formats_list_resource.rb +54 -54
  21. data/lib/mathpix/mcp/resources/hierarchical_router.rb +9 -18
  22. data/lib/mathpix/mcp/resources/latest_snip_resource.rb +22 -22
  23. data/lib/mathpix/mcp/resources/recent_snips_resource.rb +11 -10
  24. data/lib/mathpix/mcp/resources/snip_stats_resource.rb +14 -12
  25. data/lib/mathpix/mcp/server.rb +18 -18
  26. data/lib/mathpix/mcp/tools/batch_convert_tool.rb +31 -37
  27. data/lib/mathpix/mcp/tools/check_document_status_tool.rb +5 -5
  28. data/lib/mathpix/mcp/tools/convert_document_tool.rb +15 -14
  29. data/lib/mathpix/mcp/tools/convert_image_tool.rb +15 -14
  30. data/lib/mathpix/mcp/tools/convert_strokes_tool.rb +13 -13
  31. data/lib/mathpix/mcp/tools/get_account_info_tool.rb +1 -1
  32. data/lib/mathpix/mcp/tools/get_usage_tool.rb +5 -7
  33. data/lib/mathpix/mcp/tools/list_formats_tool.rb +30 -30
  34. data/lib/mathpix/mcp/tools/search_results_tool.rb +13 -14
  35. data/lib/mathpix/mcp/transports/http_streaming_transport.rb +129 -118
  36. data/lib/mathpix/mcp/transports/sse_stream_handler.rb +37 -35
  37. data/lib/mathpix/result.rb +3 -2
  38. data/lib/mathpix/version.rb +1 -1
  39. data/lib/mathpix.rb +3 -1
  40. metadata +60 -2
@@ -12,36 +12,37 @@ module Mathpix
12
12
  #
13
13
  # The geodesic path: official SDK + core gem delegation
14
14
  class ConvertImageTool < BaseTool
15
- description "Convert image (PNG, JPG, etc.) to LaTeX, text, or other formats using Mathpix OCR"
15
+ description 'Convert image (PNG, JPG, etc.) to LaTeX, text, or other formats using Mathpix OCR'
16
16
 
17
17
  input_schema(
18
18
  properties: {
19
19
  image_path: {
20
- type: "string",
21
- description: "Path to image file or URL (http:// or https://)"
20
+ type: 'string',
21
+ description: 'Path to image file or URL (http:// or https://)'
22
22
  },
23
23
  formats: {
24
- type: "array",
25
- items: { type: "string" },
26
- description: "Output formats: latex, text, mathml, asciimath, latex_styled, text_display, data, html (default: latex_styled, text)"
24
+ type: 'array',
25
+ items: { type: 'string' },
26
+ description: 'Output formats: latex, text, mathml, asciimath, latex_styled, text_display, data, html (default: latex_styled, text)'
27
27
  },
28
28
  include_line_data: {
29
- type: "boolean",
30
- description: "Include line-level bounding boxes in response"
29
+ type: 'boolean',
30
+ description: 'Include line-level bounding boxes in response'
31
31
  },
32
32
  include_word_data: {
33
- type: "boolean",
34
- description: "Include word-level bounding boxes in response"
33
+ type: 'boolean',
34
+ description: 'Include word-level bounding boxes in response'
35
35
  },
36
36
  confidence_threshold: {
37
- type: "number",
38
- description: "Minimum confidence threshold (0.0-1.0)"
37
+ type: 'number',
38
+ description: 'Minimum confidence threshold (0.0-1.0)'
39
39
  }
40
40
  },
41
- required: ["image_path"]
41
+ required: ['image_path']
42
42
  )
43
43
 
44
- def self.call(image_path:, formats: nil, include_line_data: false, include_word_data: false, confidence_threshold: nil, server_context:)
44
+ def self.call(image_path:, server_context:, formats: nil, include_line_data: false, include_word_data: false,
45
+ confidence_threshold: nil)
45
46
  safe_execute do
46
47
  client = mathpix_client(server_context)
47
48
 
@@ -12,32 +12,32 @@ module Mathpix
12
12
  #
13
13
  # The geodesic path: official SDK + stroke recognition
14
14
  class ConvertStrokesTool < BaseTool
15
- description "Convert handwritten strokes to LaTeX, text, or other formats using Mathpix OCR"
15
+ description 'Convert handwritten strokes to LaTeX, text, or other formats using Mathpix OCR'
16
16
 
17
17
  input_schema(
18
18
  properties: {
19
19
  strokes: {
20
- type: "array",
21
- description: "Array of stroke arrays, where each stroke is an array of [x, y] coordinates"
20
+ type: 'array',
21
+ description: 'Array of stroke arrays, where each stroke is an array of [x, y] coordinates'
22
22
  },
23
23
  formats: {
24
- type: "array",
25
- items: { type: "string" },
26
- description: "Output formats: latex, text, mathml, asciimath (default: latex_styled, text)"
24
+ type: 'array',
25
+ items: { type: 'string' },
26
+ description: 'Output formats: latex, text, mathml, asciimath (default: latex_styled, text)'
27
27
  },
28
28
  width: {
29
- type: "number",
30
- description: "Canvas width for stroke normalization"
29
+ type: 'number',
30
+ description: 'Canvas width for stroke normalization'
31
31
  },
32
32
  height: {
33
- type: "number",
34
- description: "Canvas height for stroke normalization"
33
+ type: 'number',
34
+ description: 'Canvas height for stroke normalization'
35
35
  }
36
36
  },
37
- required: ["strokes"]
37
+ required: ['strokes']
38
38
  )
39
39
 
40
- def self.call(strokes:, formats: nil, width: nil, height: nil, server_context:)
40
+ def self.call(strokes:, server_context:, formats: nil, width: nil, height: nil)
41
41
  safe_execute do
42
42
  client = mathpix_client(server_context)
43
43
 
@@ -57,7 +57,7 @@ module Mathpix
57
57
  # Format response
58
58
  response_data = {
59
59
  success: true,
60
- input_type: "strokes",
60
+ input_type: 'strokes',
61
61
  stroke_count: strokes.length,
62
62
  formats: output_formats,
63
63
  results: {
@@ -12,7 +12,7 @@ module Mathpix
12
12
  #
13
13
  # The geodesic path: official SDK + account management
14
14
  class GetAccountInfoTool < BaseTool
15
- description "Get Mathpix account information, plan details, and limits"
15
+ description 'Get Mathpix account information, plan details, and limits'
16
16
 
17
17
  input_schema(
18
18
  properties: {},
@@ -12,19 +12,19 @@ module Mathpix
12
12
  #
13
13
  # The geodesic path: official SDK + usage tracking
14
14
  class GetUsageTool < BaseTool
15
- description "Get Mathpix API usage statistics and remaining credits"
15
+ description 'Get Mathpix API usage statistics and remaining credits'
16
16
 
17
17
  input_schema(
18
18
  properties: {
19
19
  detailed: {
20
- type: "boolean",
21
- description: "Include detailed breakdown by operation type (default: false)"
20
+ type: 'boolean',
21
+ description: 'Include detailed breakdown by operation type (default: false)'
22
22
  }
23
23
  },
24
24
  required: []
25
25
  )
26
26
 
27
- def self.call(detailed: false, server_context:)
27
+ def self.call(server_context:, detailed: false)
28
28
  safe_execute do
29
29
  client = mathpix_client(server_context)
30
30
 
@@ -42,9 +42,7 @@ module Mathpix
42
42
  }
43
43
  }
44
44
 
45
- if detailed && usage_data['breakdown']
46
- response_data[:breakdown] = usage_data['breakdown']
47
- end
45
+ response_data[:breakdown] = usage_data['breakdown'] if detailed && usage_data['breakdown']
48
46
 
49
47
  json_response(response_data)
50
48
  rescue Mathpix::APIError => e
@@ -12,53 +12,53 @@ module Mathpix
12
12
  #
13
13
  # The geodesic path: official SDK + format catalog
14
14
  class ListFormatsTool < BaseTool
15
- description "List all available output formats for Mathpix OCR operations"
15
+ description 'List all available output formats for Mathpix OCR operations'
16
16
 
17
17
  input_schema(
18
18
  properties: {
19
19
  category: {
20
- type: "string",
21
- description: "Filter by category: image, document, or all (default: all)",
22
- enum: ["image", "document", "all"]
20
+ type: 'string',
21
+ description: 'Filter by category: image, document, or all (default: all)',
22
+ enum: %w[image document all]
23
23
  }
24
24
  },
25
25
  required: []
26
26
  )
27
27
 
28
- def self.call(category: "all", server_context:)
28
+ def self.call(server_context:, category: 'all')
29
29
  safe_execute do
30
30
  # Static format definitions
31
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" }
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
41
  ]
42
42
 
43
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" }
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
49
  ]
50
50
 
51
51
  # Filter by category
52
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
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
62
 
63
63
  # Format response
64
64
  response_data = {
@@ -67,8 +67,8 @@ module Mathpix
67
67
  count: formats.length,
68
68
  formats: formats,
69
69
  usage: {
70
- image_capture: "Use with snap() or ConvertImageTool",
71
- document_conversion: "Use with document() or ConvertDocumentTool"
70
+ image_capture: 'Use with snap() or ConvertImageTool',
71
+ document_conversion: 'Use with document() or ConvertDocumentTool'
72
72
  }
73
73
  }
74
74
 
@@ -12,31 +12,31 @@ module Mathpix
12
12
  #
13
13
  # The geodesic path: official SDK + search filtering
14
14
  class SearchResultsTool < BaseTool
15
- description "Search recent Mathpix capture results with optional text filtering"
15
+ description 'Search recent Mathpix capture results with optional text filtering'
16
16
 
17
17
  input_schema(
18
18
  properties: {
19
19
  query: {
20
- type: "string",
21
- description: "Search query to filter results by LaTeX or text content"
20
+ type: 'string',
21
+ description: 'Search query to filter results by LaTeX or text content'
22
22
  },
23
23
  limit: {
24
- type: "number",
25
- description: "Maximum number of results to return (default: 10, max: 100)"
24
+ type: 'number',
25
+ description: 'Maximum number of results to return (default: 10, max: 100)'
26
26
  },
27
27
  offset: {
28
- type: "number",
29
- description: "Offset for pagination (default: 0)"
28
+ type: 'number',
29
+ description: 'Offset for pagination (default: 0)'
30
30
  },
31
31
  include_content: {
32
- type: "boolean",
33
- description: "Include full LaTeX/text content in results (default: false)"
32
+ type: 'boolean',
33
+ description: 'Include full LaTeX/text content in results (default: false)'
34
34
  }
35
35
  },
36
36
  required: []
37
37
  )
38
38
 
39
- def self.call(query: nil, limit: 10, offset: 0, include_content: false, server_context:)
39
+ def self.call(server_context:, query: nil, limit: 10, offset: 0, include_content: false)
40
40
  safe_execute do
41
41
  client = mathpix_client(server_context)
42
42
 
@@ -53,8 +53,8 @@ module Mathpix
53
53
  if query && !query.empty?
54
54
  query_lower = query.downcase
55
55
  results = results.select do |result|
56
- (result.latex&.downcase&.include?(query_lower)) ||
57
- (result.text&.downcase&.include?(query_lower))
56
+ result.latex&.downcase&.include?(query_lower) ||
57
+ result.text&.downcase&.include?(query_lower)
58
58
  end
59
59
  end
60
60
 
@@ -98,11 +98,10 @@ module Mathpix
98
98
  end
99
99
  end
100
100
 
101
- private
102
-
103
101
  def self.truncate(text, max_length)
104
102
  return nil unless text
105
103
  return text if text.length <= max_length
104
+
106
105
  "#{text[0...max_length]}..."
107
106
  end
108
107
  end