AnotherRegularDude/ollama-web-search-mcp
If you are the rightful owner of ollama-web-search-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
A Model Context Protocol (MCP) server that provides web search capabilities to AI assistants using the Ollama web search API.
Ollama Web Search MCP Server
A Model Context Protocol (MCP) server that provides web search and web fetch capabilities to AI assistants using the Ollama web search API. This server allows LLMs to access real-time information from the web through standardized MCP interfaces with sophisticated formatting capabilities.
Quickstart: Run the MCP Server
The server needs Ruby 3.4.8, Bundler, and an OLLAMA_API_KEY.
- Install dependencies:
bundle install - Export the API key:
export OLLAMA_API_KEY="your-api-key-here"
STDIO transport
Local MCP clients can launch the STDIO server directly:
# Start via Ruby
bundle exec ruby bin/mcp_server
# Or via Rake
bundle exec rake start
HTTP transport
Expose the MCP server over HTTP (defaults to port 8080):
bundle exec ruby bin/http_server # port 8080
bundle exec ruby bin/http_server 3000 # custom port
# Or via Rake
bundle exec rake start_http # port 8080
bundle exec rake "start_http[3000]" # custom port
Sample tool requests
With the HTTP server running, you can invoke the web_search and web_fetch tools over HTTP:
Web Search Tool
curl -s http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "web_search",
"arguments": {
"query": "latest AI news",
"max_results": 2,
"truncate": true,
"max_chars": 10000
}
}
}'
Web Fetch Tool
curl -s http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "web_fetch",
"arguments": {
"url": "https://example.com",
"truncate": true,
"max_chars": 50000
}
}
}'
The response contains formatted content inside result.content[0].text.
Table of Contents
- Quickstart: Run the MCP Server
- Overview
- Features
- Prerequisites
- Installation
- Configuration
- API Reference
- Development
- Architecture
- Formatter System Details
- Contributing
- License
- Additional Resources
Overview
This Ruby-based MCP (Model Context Protocol) server implementation enables AI assistants to perform web searches and fetch web content using the Ollama web search API. The project follows the MCP specification to provide a standardized way for AI models to access external information with a sophisticated component-based formatting system.
The server exposes two primary tools:
web_search- Performs web searches with configurable result limits and content formattingweb_fetch- Retrieves content from specific URLs with advanced formatting capabilities
Features
-
Web Search Functionality:
- Perform web searches through Ollama's web search API
- Configurable maximum results (1-10)
- Content truncation with configurable character limits
- Structured search results with title, URL, and content
-
Web Fetch Functionality:
- Fetch web page content from specific URLs
- Content truncation with configurable character limits
- Related content links extraction
- Metadata preservation
-
Advanced Formatting System:
- Component-based formatting pipeline
- Content truncation with smart redistribution
- Markdown output format
- Configurable character limits
- Consistent formatting across tools
-
Technical Features:
- Type-safe data structures using Dry::Struct
- Service-oriented architecture
- Comprehensive error handling
- Support for both STDIO and HTTP transport protocols
- Configurable MCP protocol versions
Prerequisites
- Ruby 3.4.8
- Bundler gem
- Ollama API key
Installation
-
Clone the repository:
git clone https://github.com/AnotherRegularDude/ollama-web-search-mcp.git cd ollama-web-search-mcp -
Install dependencies:
bundle install
Configuration
Required Environment Variables
# API key for authenticating with the Ollama web search API
export OLLAMA_API_KEY="your-api-key-here"
Optional Environment Variables
# MCP Protocol version (optional, defaults to "2025-06-18")
# Supported versions: "2025-06-18", "2025-03-26", "2024-11-05"
export MCP_PROTOCOL_VERSION="2025-03-26"
Configuration Files
.ruby-version- Specifies Ruby version (3.4.8)Gemfile- Ruby dependenciesconfig/application.rb- Main application configuration
Defaults
- Default maximum search results: 5
- Default HTTP server port: 8080
- Default content truncation: enabled (120,000 characters max)
- Default MCP protocol version: 2025-06-18
API Reference
Web Search Tool
Tool Name: web_search
Description: Performs a web search using Ollama's web search API and returns formatted results.
Parameters:
query(string, required): The search query stringmax_results(integer, optional): Maximum results to return (1-10, default: 5)truncate(boolean, optional): Whether to truncate content (default: true)max_chars(integer, optional): Maximum characters to return (default: 120,000)
Response Format:
Search Results — "{query}"
### [Result Title 1](URL)
**URL:** URL
**Source:** search
**Content:**
---
Content snippet...
---
### [Result Title 2](URL)
**URL:** URL
**Source:** search
**Content:**
---
Content snippet...
---
Example Request:
{
"query": "latest AI news",
"max_results": 3,
"truncate": true,
"max_chars": 50000
}
Example Response (truncated):
Search Results — "latest AI news"
### [AI Breakthroughs in 2025](https://example.com/ai-2025)
**URL:** https://example.com/ai-2025
**Source:** search
**Content:**
---
Researchers have announced significant breakthroughs in neural network architectures...
---
### [New AI Legislation Proposed](https://example.com/ai-law)
**URL:** https://example.com/ai-law
**Source:** search
**Content:**
---
Governments around the world are considering new legislation to regulate AI development...
---
Web Fetch Tool
Tool Name: web_fetch
Description: Fetches web page content from a specific URL using Ollama's web fetch API.
Parameters:
url(string, required): The URL of the web page to fetchtruncate(boolean, optional): Whether to truncate the content (default: true)max_chars(integer, optional): Maximum number of characters to return (default: 120,000)
Response Format:
**Source:** fetch
**URL:** URL
**Content:**
---
Page content...
---
**Links:**
- [Link 1](Link 1)
- [Link 2](Link 2)
Example Request:
{
"url": "https://example.com",
"truncate": true,
"max_chars": 30000
}
Example Response (truncated):
**Source:** fetch
**URL:** https://example.com
**Content:**
---
This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.
More information...
---
**Links:**
- [More information...](https://example.com/more)
Development
Project Structure
├── app/
│ ├── adapters/ # External service integrations
│ │ └── ollama_gateway.rb # Ollama API communication
│ │
│ ├── cases/ # Business logic service objects
│ │ ├── formatter/ # Formatting system components
│ │ │ ├── base.rb # Base formatter class
│ │ │ ├── search_results.rb # Search results formatter
│ │ │ └── fetch_result.rb # Web fetch formatter
│ │ │
│ │ ├── node/ # Node processing services
│ │ │ ├── render_markdown.rb # Markdown rendering
│ │ │ ├── resolve_content.rb # Content resolution
│ │ │ └── truncate_content.rb # Content truncation
│ │ │
│ │ ├── search_web.rb # Web search service
│ │ └── web_fetch.rb # Web fetch service
│ │
│ ├── entities/ # Typed data structures
│ │ └── remote_content.rb # Remote content entity
│ │
│ ├── mcp_ext/ # MCP protocol implementation
│ │ ├── server_context.rb # MCP server context
│ │ ├── server_factory.rb # MCP server factory
│ │ ├── tool/ # MCP tool implementations
│ │ │ ├── base.rb # Base tool class
│ │ │ ├── web_fetch.rb # Web fetch tool
│ │ │ └── web_search.rb # Web search tool
│ │ │
│ │ └── transport_handler/ # MCP transport handlers
│ │ ├── http.rb # HTTP transport
│ │ ├── stdio.rb # STDIO transport
│ │ └── base.rb # Base transport handler
│ │
│ └── value/ # Value objects
│ ├── content_pointer.rb # Related content pointer
│ ├── node.rb # Formatting node structure
│ └── root_node.rb # Root node structure
│
├── bin/ # Executable scripts
│ ├── http_server # HTTP server entry point
│ └── mcp_server # STDIO server entry point
│
├── config/ # Application configuration
│ └── application.rb # Main app configuration
│
├── lib/ # Shared abstractions
│ ├── abstract_struct.rb # Abstract struct base class
│ ├── service_object.rb # Service object base class
│ └── types.rb # Shared type definitions
│
├── spec/ # Test files
│ ├── adapters/ # Adapter tests
│ ├── cases/ # Service object tests
│ ├── entities/ # Entity tests
│ ├── mcp_ext/ # MCP implementation tests
│ ├── config/ # Configuration tests
│ └── support/ # Test support files
│
└── tmp/ # Temporary files
Running Tests
Execute all tests using RSpec:
# Run all tests
rspec
# Run specific test files
rspec spec/cases/formatter/search_results_spec.rb
rspec spec/adapters/ollama_gateway_spec.rb
# Run tests with coverage report
COVER=true rspec
Code Quality
Run RuboCop for code style checking:
# Run RuboCop
rake rubocop
# Auto-correct RuboCop offenses
rake rubocop --auto-correct
Architecture
The project follows a layered service-oriented architecture with clear separation of concerns:
Layered Architecture
| Layer | Responsibility | Key Components |
|---|---|---|
| MCP Layer | MCP protocol implementation, tool interfaces | MCPExt::Tool::WebSearch, MCPExt::Tool::WebFetch, MCPExt::ServerFactory |
| Service Layer | Business logic, orchestration | Cases::SearchWeb, Cases::WebFetch, Cases::Formatter::Base |
| Formatter Layer | Content formatting and presentation | Cases::Formatter::SearchResults, Cases::Formatter::FetchResult, Cases::Node::RenderMarkdown |
| Adapter Layer | External API communication | Adapters::OllamaGateway |
| Data Layer | Type-safe data structures | Entities::RemoteContent, Value::ContentPointer, Value::Node |
| Value Layer | Simple value objects | Value::ContentPointer, Value::Node, Value::RootNode |
Service Layer
Business logic is encapsulated in service objects under app/cases/ that inherit from ServiceObject, a Resol-based base class providing consistent success/failure handling and Dry validation.
Key service objects:
Cases::SearchWeb- Orchestrates web searches, interacting with the Ollama gateway and mapping resultsCases::WebFetch- Orchestrates web content fetching, interacting with the Ollama gateway and mapping resultsCases::Formatter::Base- Base class for all formatters with content truncation capabilitiesCases::Formatter::SearchResults- Formats search results using the component-based systemCases::Formatter::FetchResult- Formats web fetch results using the component-based system
Adapter Layer
External service integrations are encapsulated in adapters:
Adapters::OllamaGateway- Handles all communication with the Ollama web search API- Performs web search requests
- Performs web fetch requests
- Handles authentication and error responses
Data Layer
Entities are implemented using Dry::Struct for type-safe data structures:
Entities::RemoteContent- Represents both search results and fetched content- Attributes:
title,url,content,related_content,source_type
- Attributes:
Value::ContentPointer- Represents related content linksValue::NodeandValue::RootNode- Tree structures for formatting
Formatter System
The project includes a sophisticated component-based formatter system that provides:
- Component-based formatting with reusable components
- Content truncation with smart redistribution
- Markdown rendering for human-readable output
- Consistent formatting across different output types
MCP Layer
The MCP layer (app/mcp_ext/) contains:
MCPExt::Tool::WebSearch- Implements theweb_searchMCP tool- Validates parameters
- Calls the service layer
- Formats results using the formatter system
MCPExt::Tool::WebFetch- Implements theweb_fetchMCP tool- Validates parameters
- Calls the service layer
- Formats results using the formatter system
MCPExt::ServerFactory- Creates configured MCP serversMCPExt::TransportHandler- Routes transport configuration to appropriate handlers (STDIO or HTTP)
Formatter System Details
Component-Based Formatting
The formatter system uses a component-based approach to build structured output:
-
Formatter Classes (
Cases::Formatter::Base,Cases::Formatter::SearchResults,Cases::Formatter::FetchResult)- Build a schema structure using
Value::NodeandValue::RootNodeobjects - Handle content truncation based on character limits
- Process the schema through the rendering pipeline
- Build a schema structure using
-
Node Processing (
Cases::Node::RenderMarkdown,Cases::Node::ResolveContent,Cases::Node::TruncateContent)- Render node structures to Markdown format
- Resolve content text from nodes
- Truncate content based on available character budget
-
Node Types
:header- Section headers:result- Individual search result cards:metadata- Source and URL information:content- Main content blocks:links- Lists of related links
Truncation System
The truncation system provides intelligent content truncation:
-
Truncation Process:
- Calculate formatting overhead (non-content characters)
- Determine available content budget
- Distribute budget equally among content sections
- Redistribute unused budget to remaining sections
-
Key Features:
- Smart redistribution of character budget
- Equal distribution among content sections
- Graceful handling of small token limits
-
Implementation:
Cases::Node::TruncateContent- Handles the truncation logic- Integrated into the formatter pipeline through
Cases::Formatter::Base
Node Structure
The formatter system uses a tree structure of nodes:
-
RootNode (
Value::RootNode)- Contains metadata about the content
- Has children nodes representing different sections
-
Node (
Value::Node)type: Symbol representing the node type (:header,:result,:metadata,:content,:links)data: Hash containing node-specific datachildren: Array of child nodes
-
Example Structure for Search Results:
Value::RootNode.new( metadata: { query: "ruby programming", total_results: 2 }, children: [ Value::Node.new( type: :header, data: { text: "Search Results — \"ruby programming\"" } ), Value::Node.new( type: :result, data: { title: "Ruby Programming", url: "https://ruby-lang.org", source: :search }, children: [ Value::Node.new( type: :content, data: { text: "Ruby is a dynamic programming language..." } ) ] ) ] )
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for your changes
- Run tests to ensure they pass
- Update documentation as needed
- Submit a pull request
Development Guidelines
- Code Style: Follow existing Ruby style conventions as enforced by RuboCop
- Type Safety: Use
dry-rbgems for type safety and validation - Service Objects: Implement business logic in service objects under
app/cases/ - Formatter System: Use the component-based formatter system for all output formatting
- Testing: Write comprehensive tests for all new functionality
- Documentation: Maintain consistent YARD documentation for classes and methods
Testing JSON Structures
When testing HTTP requests and responses that involve JSON data, use these best practices:
-
Use
be_json_asfor complete JSON structure matching:expect(request.body).to be_json_as(query: "test query", max_results: 5) -
Use
be_json_includingfor partial JSON matching:expect(response.body).to be_json_including(status: "success") -
Use symbols as hash keys in test expectations (the matchers handle symbol-to-string conversion):
expect(result.to_json).to be_json_as( title: "Example Page", url: "https://example.com", related_content: [ { title: "Related Link", url: "https://example.com/related" } ] )
License
This project is licensed under the MIT License. See the file for details.