MCP Tools API Reference
This page documents the MCP (Model Context Protocol) JSON-RPC API for programmatic integration.
If you're setting up ChatGPT or Claude, see the MCP Connectors Guide instead.
Overview
The MCP server implements the Streamable HTTP transport and exposes the synjar_search tool for searching your Synjar knowledge base via JSON-RPC 2.0 protocol.
Base URL: https://api.synjar.com/mcp/{token}
Protocol: JSON-RPC 2.0 / MCP Streamable HTTP
Methods: POST (JSON-RPC), OPTIONS (CORS), GET (returns 405)
Content-Type: application/json
Supported JSON-RPC Methods:
| Method | Description |
|---|---|
initialize | Returns server capabilities and protocol version |
tools/list | Returns available tools and their schemas |
tools/call | Executes the synjar_search tool |
Typical MCP Session Flow
Client Server
| |
|--initialize------------------>|
|<--capabilities----------------|
| |
|--tools/list------------------>|
|<--[synjar_search]-------------|
| |
|--tools/call (search)--------->|
|<--results---------------------|
| |
|--tools/call (search)--------->|
|<--results---------------------|
The typical session starts with initialize to negotiate capabilities, then tools/list to discover available tools, followed by one or more tools/call requests to perform searches.
Authentication
Authentication is done via the token in the URL path:
POST https://api.synjar.com/mcp/{PUBLIC_TOKEN}
The {PUBLIC_TOKEN} is your Search Link's public token (64-character hex string).
- Tokens are sensitive—treat them like API keys
- Use HTTPS only (TLS 1.3)
- Store tokens securely (environment variables, secrets manager)
Method: initialize
Returns server capabilities and protocol version. This is typically the first method called by MCP clients.
Request Format
{
"jsonrpc": "2.0",
"id": "init-1",
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "ChatGPT",
"version": "1.0"
}
}
}
Response Format
{
"jsonrpc": "2.0",
"id": "init-1",
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "Synjar MCP Server",
"version": "1.0.0"
}
}
}
Method: tools/list
Returns available tools and their JSON Schema definitions. Use this to discover what tools are available.
Request Format
{
"jsonrpc": "2.0",
"id": "list-1",
"method": "tools/list",
"params": {}
}
Response Format
{
"jsonrpc": "2.0",
"id": "list-1",
"result": {
"tools": [
{
"name": "synjar_search",
"description": "Search the Synjar knowledge base. Available tags: docs, faq",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Natural language search query (2-256 characters)"
},
"limit": {
"type": "number",
"description": "Maximum results to return (default: 5, max: 20)"
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Filter by document tags. Allowed: docs, faq"
}
},
"required": ["query"]
}
}
]
}
}
The description field dynamically includes allowed tags from your Search Link configuration.
Tool: synjar_search
Search a Synjar knowledge base for relevant documents and chunks.
Request Format
{
"jsonrpc": "2.0",
"id": "unique-request-id",
"method": "tools/call",
"params": {
"name": "synjar_search",
"arguments": {
"query": "your search query",
"limit": 5,
"tags": ["optional", "filter", "tags"]
}
}
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Natural language search query (2-256 characters) |
limit | number | No | Max results to return (default: 5, max: 20) |
tags | string[] | No | Filter by document tags (must be subset of allowed tags) |
Response Format
{
"jsonrpc": "2.0",
"id": "unique-request-id",
"result": {
"content": [
{
"type": "text",
"text": "{\"results\":[{\"title\":\"...\",\"content\":\"...\",\"score\":0.95,\"sourceUrl\":\"...\"}],\"totalCount\":3}"
}
]
}
}
The text field contains a JSON-encoded object with:
| Field | Type | Description |
|---|---|---|
results | array | Array of search results |
results[].title | string | Document title |
results[].content | string | Relevant chunk content |
results[].score | number | Relevance score (0-1, higher is better) |
results[].sourceUrl | string | URL to source document (if available) |
totalCount | number | Total number of results returned |
Error Codes
MCP uses standard JSON-RPC error codes:
| HTTP | JSON-RPC Code | Error | Description |
|---|---|---|---|
| 400 | -32700 | Parse error | Invalid JSON |
| 400 | -32600 | Invalid request | Invalid JSON-RPC structure |
| 400 | -32602 | Invalid params | Parameter validation failed |
| 403 | -32002 | Forbidden | Invalid, inactive, or expired token |
| 429 | -32000 | Rate limit | Rate limit exceeded |
| 500 | -32603 | Internal error | Server error |
Error Response Format
{
"jsonrpc": "2.0",
"id": "unique-request-id",
"error": {
"code": -32602,
"message": "Query must be 2-256 characters"
}
}
Examples
Example 1: Basic Search
Request:
curl -X POST https://api.synjar.com/mcp/abc123def456... \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "req-1",
"method": "tools/call",
"params": {
"name": "synjar_search",
"arguments": {
"query": "refund policy"
}
}
}'
Response:
{
"jsonrpc": "2.0",
"id": "req-1",
"result": {
"content": [
{
"type": "text",
"text": "{\"results\":[{\"title\":\"Refund Policy\",\"content\":\"Full refund within 30 days of purchase...\",\"score\":0.95,\"sourceUrl\":\"https://app.synjar.com/docs/123\"}],\"totalCount\":1}"
}
]
}
}
Example 2: Search with Tags Filter
Request:
{
"jsonrpc": "2.0",
"id": "req-2",
"method": "tools/call",
"params": {
"name": "synjar_search",
"arguments": {
"query": "installation steps",
"limit": 10,
"tags": ["documentation", "setup"]
}
}
}
Response:
{
"jsonrpc": "2.0",
"id": "req-2",
"result": {
"content": [
{
"type": "text",
"text": "{\"results\":[{\"title\":\"Getting Started Guide\",\"content\":\"Follow these installation steps to set up Synjar...\",\"score\":0.92,\"sourceUrl\":\"https://app.synjar.com/docs/456\"},{\"title\":\"Docker Installation\",\"content\":\"Run docker-compose up to start the application...\",\"score\":0.87,\"sourceUrl\":\"https://app.synjar.com/docs/789\"}],\"totalCount\":2}"
}
]
}
}
Note: Only documents with matching tags (documentation OR setup) are returned. The tags filter is additive (OR logic), and all requested tags must be in the Search Link's allowedTags list.
Example 3: Invalid Token Error
Request:
curl -X POST https://api.synjar.com/mcp/invalid-token \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"req-3","method":"tools/call","params":{"name":"synjar_search","arguments":{"query":"test"}}}'
Response:
{
"jsonrpc": "2.0",
"id": "req-3",
"error": {
"code": -32002,
"message": "Invalid or expired token"
}
}
Example 4: Rate Limit Error
Response:
{
"jsonrpc": "2.0",
"id": "req-4",
"error": {
"code": -32000,
"message": "Rate limit exceeded (30 requests/minute)",
"data": {
"retryAfter": 45
}
}
}
Rate Limits
Two-tier rate limiting:
- Per IP: 100 requests/minute (prevents enumeration attacks)
- Per PublicLink: 30 requests/minute (prevents abuse of specific links)
Rate limit headers:
X-RateLimit-Limit: 30 # Max requests per window
X-RateLimit-Remaining: 25 # Requests left in current window
X-RateLimit-Reset: 1704067200 # Unix timestamp when window resets
Retry-After: 45 # Seconds to wait (only on 429)
Client retry logic:
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return retry(request);
}
Client Libraries
JavaScript/TypeScript
async function searchSynjar(
token: string,
query: string,
limit = 5
): Promise<any> {
const response = await fetch(`https://api.synjar.com/mcp/${token}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: Math.random().toString(),
method: 'tools/call',
params: {
name: 'synjar_search',
arguments: { query, limit },
},
}),
});
const data = await response.json();
if (data.error) {
throw new Error(data.error.message);
}
return JSON.parse(data.result.content[0].text);
}
// Usage
const results = await searchSynjar(
'abc123def456...',
'refund policy',
10
);
console.log(results.results);
Python
import requests
import json
def search_synjar(token: str, query: str, limit: int = 5) -> dict:
url = f"https://api.synjar.com/mcp/{token}"
payload = {
"jsonrpc": "2.0",
"id": "req-1",
"method": "tools/call",
"params": {
"name": "synjar_search",
"arguments": {
"query": query,
"limit": limit
}
}
}
response = requests.post(url, json=payload)
data = response.json()
if "error" in data:
raise Exception(data["error"]["message"])
return json.loads(data["result"]["content"][0]["text"])
# Usage
results = search_synjar("abc123def456...", "refund policy", 10)
for result in results["results"]:
print(f"{result['title']}: {result['score']}")
Error Handling
Common Errors
| Code | Name | Description |
|---|---|---|
| -32600 | Invalid Request | Malformed JSON-RPC request |
| -32601 | Method Not Found | Unknown method name |
| -32602 | Invalid Params | Invalid method parameters |
| -32603 | Internal Error | Server-side error |
| 429 | Rate Limited | Too many requests (100/min per token) |
TypeScript Error Handling Example
async function searchWithRetry(token: string, query: string, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch(`https://api.synjar.com/mcp/${token}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: { name: 'synjar_search', arguments: { query } }
})
});
if (response.status === 429) {
// Rate limited - wait and retry
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
continue;
}
const json = await response.json();
if (json.error) {
throw new Error(`MCP Error ${json.error.code}: ${json.error.message}`);
}
return json.result;
}
throw new Error('Max retries exceeded');
}
Python Error Handling Example
import requests
import time
def search_with_retry(token: str, query: str, retries: int = 3) -> dict:
url = f"https://api.synjar.com/mcp/{token}"
payload = {
"jsonrpc": "2.0",
"id": "req-1",
"method": "tools/call",
"params": {
"name": "synjar_search",
"arguments": {"query": query}
}
}
for i in range(retries):
response = requests.post(url, json=payload)
if response.status_code == 429:
# Rate limited - wait and retry with exponential backoff
time.sleep((i + 1) * 1)
continue
data = response.json()
if "error" in data:
raise Exception(f"MCP Error {data['error']['code']}: {data['error']['message']}")
return data["result"]
raise Exception("Max retries exceeded")
Privacy & Security
Query Text Storage
By default, query text is NOT stored. Only aggregate counts are tracked.
When historyMode = OFF (default):
- ✅ Request count tracked
- ✅ Latency metrics collected
- ❌ Query text NOT stored
When historyMode = ON:
- ✅ Request count tracked
- ✅ Latency metrics collected
- ✅ Query text stored for 90 days (auto-deleted after)
IP & User-Agent Hashing
IP addresses and User-Agent strings are hashed (SHA-256) for privacy:
- One-way hashing (cannot be reversed)
- Used only for abuse detection
- NOT shared with third parties
Privacy Compliance
- GDPR Article 17 (Right to Erasure): Use workspace anonymization workflow to delete all personal data
- CCPA compliance: Query text is considered personal data when stored (requires disclosure)
- 90-day retention: Query text is automatically deleted after 90 days
- Full details: Privacy Policy
Log Scrubbing
MCP tokens and query text are scrubbed from access logs:
GET /mcp/abc123... → GET /mcp/[REDACTED]
Troubleshooting
Invalid JSON-RPC Request
Error: -32600 Invalid request
Common causes:
- Missing
jsonrpc: "2.0"field - Wrong method (use
tools/call) - Missing
paramsobject
Fix: Ensure your request matches the Request Format exactly.
Invalid Parameters
Error: -32602 Invalid params
Common causes:
- Query too short (< 2 chars) or too long (> 256 chars)
- Limit out of range (must be 1-20)
- Tags not in allowed tags list
Fix: Validate parameters before sending request.
Connection Timeout
Problem: Request times out after 30 seconds.
Causes:
- Large dataset (millions of chunks)
- Complex query (very long)
- Server overload
Fix:
- Reduce
limitparameter - Simplify query
- Add tags filter to narrow search scope
Best Practices
- Reuse connections: Use HTTP keep-alive for multiple requests
- Handle rate limits: Implement exponential backoff when you hit 429
- Validate parameters: Check query length and limit range before sending
- Store tokens securely: Use environment variables, never commit to git
- Monitor usage: Track your API usage to avoid unexpected rate limits
Changelog
-
2026-01-05: MCP Streamable HTTP support
- Added
initializemethod for server capabilities - Added
tools/listmethod for tool discovery - CORS disabled (CLI/backend access only)
- Rate limiting per token (McpThrottlerGuard)
- Protocol version:
2025-06-18
- Added
-
2026-01-01: Initial MCP server release
synjar_searchtool- JSON-RPC 2.0 protocol
- Rate limiting (100 req/min per IP, 30 req/min per link)
- Privacy controls (historyMode)
Related Documentation
- MCP Connectors Guide - For ChatGPT/Claude setup
- Search Links API - Direct search API
- MCP Specification - Official MCP protocol docs
Questions? Contact support or open an issue on GitHub.