Skip to main content
Glama
estrenuo

OmniFocus MCP Server

by estrenuo

OmniFocus MCP Server

JXA(JavaScript for Automation)を介してAIアシスタントがmacOS上のOmniFocusを操作できるようにするModel Context Protocol(MCP)サーバーです。

機能

このMCPサーバーはOmniFocusの機能へのアクセスを提供します:

タスク管理

  • 受信トレイのタスク一覧 - 受信トレイ内のタスクを表示・フィルタリング(複数タグでのフィルタリング対応)

  • タスクの作成 - 全プロパティ(期限日、予定日、タグ、メモ、サブタスク、繰り返し)に対応した新規タスクの追加

  • タスクの更新 - 名前、メモ、日付、フラグ、見積もり、繰り返しの変更、別プロジェクトへの移動

  • タスクの完了/ドロップ - タスクを個別または一括で完了またはドロップに設定

  • タスクの削除 - タスクを完全に削除

  • タスクメモの更新 - タスクのメモを置換、クリア、追記

  • 期限切れタスクの取得 - 期間内に期限が来るタスクを検索

  • 予定タスクの取得 - 期間内に予定されているタスクを検索

  • フラグ付きタスクの取得 - フラグ付きの全項目を一覧表示

  • タスクへのタグ追加/削除 - タスクのタグを個別または一括で管理

プロジェクト管理

  • プロジェクト一覧 - ステータスフィルタリング付きでプロジェクトを表示

  • プロジェクトのタスク取得 - プロジェクトに属するすべてのタスクを一覧表示

  • プロジェクトの作成 - フォルダ配置、ステータス、日付、順次実行モード、レビュー間隔を指定して新規作成

  • プロジェクトの更新 - 名前、メモ、ステータス、フラグ、日付、順次実行モード、レビュー間隔を変更

  • プロジェクトの削除 - プロジェクトとそのタスクを削除

  • プロジェクトメモの更新 - プロジェクトのメモを置換、クリア、追記

  • レビュー対象プロジェクトの取得 - レビューが必要なプロジェクトを、未完了タスク付きで検索

  • レビュー済みにマーク - プロジェクトのレビューステータスと次回レビュー日を更新

  • 一括レビュー済みマーク - 複数のプロジェクトを一度に効率的にレビュー

整理

  • フォルダ一覧 - フォルダ階層を表示

  • フォルダの作成/名前変更/削除 - フォルダツリーを管理(ネストしたフォルダ対応)

  • タグ一覧 - すべてのタグを表示

  • パースペクティブ一覧 - 組み込みおよびカスタムパースペクティブを表示

  • パースペクティブのタスク取得 - 指定したパースペクティブに表示されるタスクを一覧表示

検索

  • ユニバーサル検索 - タスク、プロジェクト、フォルダ、タグを横断して検索

安全性の保証

  • 重複名での誤動作なし。 OmniFocusでは2つのプロジェクト(またはタスク)が同じ名前を共有できます。名前の検索はすべての一致を収集し、複数ある場合は一致するIDとともに失敗するため、名前変更、移動、削除が誤った項目に対して成功として報告されることはありません。

  • 変更は検証されます。 JXAが静かに失敗する可能性のある操作(特にプロジェクト間のタスク移動)は、同じスクリプト内で結果を読み戻すため、失敗した移動は成功ではなくエラーとして報告されます。

Related MCP server: OmniFocus MCP Server

要件

  • macOS(OmniFocusはmacOS/iOSのみ対応、このサーバーはJXAを使用)

  • OmniFocus 3以上がインストールされていること

  • Node.js 18以上

  • ターミナル/クライアントアプリでAutomation権限が有効になっていること

インストール

  1. このリポジトリをクローンまたはダウンロードします:

    cd omnifocus-mcp-server
  2. 依存関係をインストール:

    npm install
  3. TypeScriptをビルド:

    npm run build
  4. MCPクライアントがサーバーを使用するように設定します(下記の「設定」を参照)

設定

Claude Desktop

Claude Desktopの設定ファイル(~/Library/Application Support/Claude/claude_desktop_config.json)に追加します:

{
  "mcpServers": {
    "omnifocus": {
      "command": "/opt/homebrew/bin/node",
      "args": ["/path/to/omnifocus-mcp-server/dist/index.js"]
    }
  }
}

nodeバイナリへの絶対パスを使用してください。素の"node"はGUIセッションのPATHで解決されますが、そこにはHomebrewやバージョンマネージャーのシムが含まれていないため、ターミナルからは正常に動作しても「サーバーに到達できません」というエラーでサーバーが起動しません。which nodeで確認できます。

その他のMCPクライアント

サーバーはデフォルトでstdioトランスポートを使用するため、クライアントが以下を起動するように設定します:

node /path/to/omnifocus-mcp-server/dist/index.js

リモートアクセス(HTTPトランスポート)

リモートクライアント(最も重要なのはclaude.aiのカスタムコネクタで、Claude iOSアプリがMCPサーバーに到達する方法です)の場合、サーバーはStreamable HTTPエンドポイントとして実行できます:

MCP_TRANSPORT=http \
MCP_AUTH_TOKEN="$(openssl rand -hex 32)" \
node /path/to/omnifocus-mcp-server/dist/index.js

環境変数:

変数

デフォルト

目的

MCP_TRANSPORT

stdio

HTTPトランスポートを有効にするにはhttpに設定

MCP_HTTP_PORT

3000

リッスンするポート

MCP_HTTP_HOST

127.0.0.1

バインドアドレス(ループバックのままにし、トンネル経由で公開)

MCP_AUTH_TOKEN

必須の共有シークレット。これがないとサーバーは起動を拒否

MCP_PUBLIC_URL

サーバーが到達可能な公開HTTPSオリジン(例:https://your-tunnel-host)。claude.ai / Claude Desktopの「Connectors」UIに必要なOAuthレイヤーを有効にするにはこれを設定します。静的トークンのみを必要とする直接/プログラムによるクライアントの場合は未設定のままにします。

OMNIFOCUS_SCRIPT_TIMEOUT_MS

60000

ハングしたJXAスクリプトを強制終了(両方のトランスポートに適用)

MCPエンドポイントは/mcpです。認証はAuthorization: Bearer <token>ヘッダー、またはカスタムヘッダーを送信できないクライアントの場合はパスセグメント(/mcp/<token>)としてのトークンの両方を受け付けます。GET /healthは認証不要です。

claude.ai / iOSアプリからアクセスする。 カスタムコネクタは(デバイスではなく)Anthropicのクラウドから接続するため、エンドポイントはHTTPSで公開されている必要があります。Cloudflare TunnelまたはTailscale Funnelのどちらでも機能します。どちらの場合も、ローカルプロセスがアウトバウンド接続を行うため、ポートが開くことはありません。(Funnelなしの通常のTailscaleでは機能しません。自分のテイルネットにしか到達できず、Anthropicのクラウドはそこにいないためです。)必要に応じて、Cloudflare WAFルールでAnthropicのアウトバウンドIPレンジ(160.79.104.0/21)にアクセスを制限できます。

claude.aiとClaude Desktopの「Connectors」UI(生のMCP設定や直接のAPIクライアントとは異なります)は、リモートサーバーを呼び出す前に常に完全なOAuthハンドシェイクを実行します。静的トークンだけでは受け付けられません(URLに埋め込んでも同様です)。MCP_PUBLIC_URLをトンネルの公開オリジンに設定すると、自己発行のOAuthレイヤーが有効になり、同じ静的シークレットでアクセスを制限しながらこれを満たします(詳細はoauth.ts / CLAUDE.mdを参照)。設定したら、パストークンURL(https://your-tunnel-host/mcp/<token>)を使用して、設定→コネクタでコネクタを追加します。「接続」ステップでOAuthハンドシェイクが自動的に完了します。トンネルが/を別のローカルサービスにプロキシしている場合は、/authorize/token/register/.well-known/*もこのサーバーにマッピングされていることを確認してください。そうしないとOAuthリクエストがここに届きません。

MacはOmniFocusを起動したままスリープさせないでください(caffeinate -sまたはAmphetamineを使用)。

セッションセマンティクス - シングルクライアントのみ。 HTTPトランスポートは一度に1つのセッションを提供します。新しいinitializeは前のセッションを置き換えます。すべてのツール呼び出しはステートレスであり、仕様準拠のクライアントは存在しないセッションに対して404を受け取ると再初期化するため、順番に呼び出す単一のクライアントは問題なく機能します。この404は、サーバーがセッションをまったく保持していない場合もカバーします。これはサーバー再起動後にすべてのクライアントで発生し、デッドエンドポイントとして扱うのではなく再初期化します。

2つのクライアントを同時に使用することはできません。公開エンドポイントに対して2つの同時initializetools/listシーケンスを発火させて再現しました。1つは一貫して404になります。基盤となるMCP SDKは単一のトランスポートを共有サーバーインスタンスにバインドするため、負けたセッションは追い出され、進行中のリクエストは404になるかハングします。これを修正するには、現在のシングルトンへの登録パターンではなく、セッションごとのMcpServerインスタンスが必要です。現在のところ予定はありません。詳細はCLAUDE.mdを参照してください。

「接続できない」クライアントのトラブルシューティング。 クライアントはすべてのリモート障害を1つのあいまいなメッセージにまとめてしまうため、クライアントの文言ではなく、このサーバーのログ(launch agentのStandardErrorPath)を読んでください。ステータスコードが、無関係な3つのうちのどれが原因かを示しています:

ログに表示される内容

意味

修正

↳ path token rejected: got N chars …

クライアントのURLのトークンが間違っているか切り詰められている

<MCP_PUBLIC_URL>/mcp/<token>全体をコピーし直す。再入力しないこと

↳ authorize rejected: resource … !== …

同じ原因。OAuth側から見た場合。/authorize → 302の後に/tokenがない場合は常にこれを意味する

上記と同じ

[…] → 404 の後に新しいinitialize

再起動またはセッション引き継ぎ後の通常のリカバリ

何もしない。クライアントが自動的に再初期化する

[initialize] → 200 — client: …

正常に動作中。クライアント名がどれかを識別する

ログに何も表示されない場合、リクエストはこのサーバーに到達していません。このコードではなく、トンネルのパスマッピングを確認してください。

権限

初回使用時に、macOSが自動化アクセスを許可するかどうかを尋ねます:

  1. システム環境設定セキュリティとプライバシープライバシー自動化に移動

  2. ターミナルまたはClaude DesktopがOmniFocusを制御する権限を有効にする

ツールリファレンス

全31ツールを以下に領域別に示します。

omnifocus_list_inbox

受信トレイ内のタスクを一覧表示します。オプションでタグでフィルタリングできます。

{
  "includeCompleted": false,
  "limit": 50,
  "tags": ["Work", "Urgent"],
  "tagMatchMode": "all"
}

tagMatchMode"all"(タスクがリストされたすべてのタグを持つ、デフォルト)、"any"(少なくとも1つ)、"none"(どれも持たない)のいずれかです。tagsが指定された場合にのみ適用されます。同じ2つのパラメータはomnifocus_get_due_tasksomnifocus_get_flagged_tasksomnifocus_get_planned_tasksでも機能します。

omnifocus_list_projects

フィルタリング付きでプロジェクトを一覧表示します。

{
  "status": "active",
  "folderName": "Work",
  "limit": 50
}

omnifocus_get_project_tasks

1つのプロジェクトに属するすべてのタスクを取得します。

{
  "projectId": "abc123",
  "includeCompleted": false,
  "limit": 100
}

omnifocus_create_project

プロジェクトを作成します。オプションでフォルダ内に作成できます。

{
  "name": "Website redesign",
  "note": "Q1 initiative",
  "folderName": "Work",
  "dueDate": "2024-03-31T17:00:00",
  "deferDate": "2024-01-15T09:00:00",
  "flagged": false,
  "sequential": false,
  "status": "active",
  "reviewIntervalDays": 7
}

status"active" (default)、"on hold" 録: 完了}, 中断} とします。sequential: false (default) は並列プロジェクトを作成します。オプションのkwargsはタスク作成と同様です。

omnifocus_update_project

プロジェクトのプロパティを更新します。プロジェクトの特定は projectId または projectName で行います(IDが優先)。

{
  "projectId": "abc123",
  "name": "Website redesign v2",
  "status": "on hold",
  "flagged": true,
  "dueDate": null,
  "sequential": true,
  "reviewIntervalDays": 14
}

notedueDatedeferDatenull を渡すとクリアされます。プロジェクトを他のフォルダに移動することはできません(JXAの制限)。

omnifocus_delete_project

プロジェクトとそのタスクを削除します。プロジェクトの特定は projectId または projectName で行います(IDが優先)。

{
  "projectId": "abc123"
}

omnifocus_list_folders

すべてのフォルダを一覧表示します。

{
  "status": "active",
  "limit": 50
}

omnifocus_create_folder

トップレベルまたはネストしたフォルダを作成します。

{
  "name": "Clients",
  "parentFolderName": "Work"
}

omnifocus_update_folder

フォルダ名を変更します。フォルダの特定は folderId または folderName で行います(IDが優先)。フォルダを別のフォルダに移動することはできません(JXAの制限)。

{
  "folderName": "Clients",
  "name": "Key clients"
}

omnifocus_delete_folder

フォルダとその中のすべてを削除します。フォルダの特定は folderId または folderName で行います(IDが優先)。

{
  "folderId": "abc123"
}

omnifocus_list_tags

すべてのタグを一覧表示します。

{
  "status": "active",
  "limit": 50
}

omnifocus_list_perspectives

(ビルトインおよびカスタムの)パースペクティブを一覧表示します。

{
  "limit": 50
}

omnifocus_get_perspective_tasks

特定のパースペクティブに表示されるタスクを取得します。

{
  "perspectiveName": "Next",
  "limit": 50
}

omnifocus_create_task

新しいタスクを作成します。

{
  "name": "Review quarterly report",
  "note": "Check all sections",
  "projectName": "Work",
  "dueDate": "2024-12-31T17:00:00",
  "deferDate": "2024-12-01T09:00:00",
  "plannedDate": "2024-12-15T09:00:00",
  "flagged": true,
  "estimatedMinutes": 60,
  "tagNames": ["Review", "Important"],
  "parentTaskId": "xyz789",
  "recurrence": {
    "frequency": "weekly",
    "interval": 1,
    "daysOfWeek": ["Monday", "Thursday"],
    "repeatFrom": "due-date"
  }
}

予定日と期日の違い:

  • dueDate:タスクを完了しなければならない日(締め切り)

  • plannedDate:タスクに取り組む予定の日(計画)

  • この区別は、締め切りと予定された作業時間を分離する上で重要です。

繰り返し: frequency"daily""weekly""monthly""yearly" のいずれかです。毎週は daysOfWeek、毎月は dayOfMonth(1〜31)、毎年は monthOfYear(1〜12)を使用します。repeatFrom"due-date"(デフォルト)または "completion-date" です。

サブタスク: parentTaskId を渡すと、既存のタスクの子タスクとして作成されます。

omnifocus_update_task

既存のタスクを更新します。タスクの特定は taskId または taskName で行います(IDが優先)。

{
  "taskId": "abc123",
  "name": "Review quarterly report (final)",
  "note": null,
  "dueDate": "2024-12-20T17:00:00",
  "flagged": true,
  "estimatedMinutes": 45,
  "projectName": "Work"
}
  • notedueDatedeferDateplannedDatenull を渡すとクリアされます。estimatedMinutes: 0 で見積もりをクリアします。

  • projectId / projectName を指定すると、タスクがそのプロジェクトに移動されます(サブタスクも一緒に移動します)。移動後には検証が行われるため、失敗した場合は偽の成功ではなくエラーとして報告されます。

  • recurrencecreate_task と同じオブジェクトを受け取ります。recurrence: null または clearRecurrence: true で繰り返しをオフにします。

omnifocus_delete_task

タスクを削除します。タスクの特定は taskId または taskName で行います(IDが優先)。

{
  "taskId": "abc123"
}

omnifocus_update_task_note

タスクのメモを置換、クリア、または追記します。タスクの特定は taskId または taskName で行います(IDが優先)。

{
  "taskId": "abc123",
  "note": "Added after the call.",
  "append": true
}

空の note 文字列はメモをクリアします。

omnifocus_complete_task

タスクを完了または中断にします。タスクはIDまたは名前のどちらでも特定できます。

{
  "taskId": "abc123",
  "action": "complete"
}

またはタスク名を使用する場合:

{
  "taskName": "Write documentation",
  "action": "complete"
}

アクションは "complete"(デフォルト)または "drop" です。taskIdtaskName の両方が指定された場合は、taskId が優先されます。

繰り返しタスクを中断すると、最初に繰り返しルールがクリアされるため、次の発生に繰り越されるのではなく、シリーズが実際に停止します。

omnifocus_batch_complete_task

1回の呼び出しで最大100件のタスクをIDで完了または中断します。

{
  "taskIds": ["id1", "id2", "id3"],
  "action": "complete"
}

omnifocus_add_tag_to_task

タスクにタグを追加します。タスクはIDまたは名前のどちらでも特定できます。

{
  "taskId": "abc123",
  "tagName": "Urgent"
}

またはタスク名を使用する場合:

{
  "taskName": "Write report",
  "tagName": "Urgent"
}

taskIdtaskName の両方が指定された場合は、taskId が優先されます。

omnifocus_remove_tag_from_task

タスクからタグを削除します。タスクはIDまたは名前のどちらでも特定できます。

{
  "taskId": "abc123",
  "tagName": "Urgent"
}

またはタスク名を使用する場合:

{
  "taskName": "Old task",
  "tagName": "Done"
}

taskIdtaskName の両方が指定された場合は、taskId が優先されます。

omnifocus_batch_add_tag

既存の1つのタグを、IDで指定した最大100件のタスクに追加します。

{
  "taskIds": ["id1", "id2", "id3"],
  "tagName": "Urgent"
}

omnifocus_batch_remove_tag

1つのタグを、IDで指定した最大100件のタスクから削除します。

{
  "taskIds": ["id1", "id2", "id3"],
  "tagName": "Urgent"
}

omnifocus_update_project_note

プロジェクトのメモを置換、クリア、または追記します。プロジェクトの特定は projectId または projectName で行います(IDが優先)。

{
  "projectName": "Website redesign",
  "note": "Kickoff moved to March.",
  "append": false
}

omnifocus_search

OmniFocus全体を検索します。

{
  "query": "report",
  "searchType": "all",
  "limit": 20
}

omnifocus_get_due_tasks

期間内に期限が来るタスクを取得します。

{
  "daysAhead": 7,
  "includeOverdue": true,
  "limit": 50
}

omnifocus_get_flagged_tasks

フラグ付きタスクを取得します。

{
  "includeCompleted": false,
  "limit": 50
}

omnifocus_get_planned_tasks

期間内に予定されているタスクを取得します。

{
  "daysAhead": 7,
  "includeOverdue": true,
  "limit": 50
}

omnifocus_get_projects_for_review

次のレビュー日に基づいて、レビューが必要なプロジェクトを取得します。レビューワークフローを実践しているGTD実践者に最適です。

{
  "daysAhead": 0,
  "status": "active",
  "limit": 50,
  "includeTasks": true,
  "taskLimit": 50
}

パラメータ:

  • daysAhead: 将来をどのくらい先まで見るか(0 = 期限切れのレビューのみ)

  • status: プロジェクトのステータスでフィルタリング("active"、"done"、"dropped"、"onHold"、"all")

  • limit: 返すプロジェクトの最大数(1〜500)

  • includeTasks: 結果に各プロジェクトの未完了タスクを含めるかどうか(デフォルトはfalse)— これにより、レビュー1回分を、プロジェクトごとにフォローアップ呼び出しをする代わりに、1回の呼び出しで済ませられます

  • taskLimit: includeTasks が true の場合の、プロジェクトあたりの最大タスク数(1〜200、デフォルトは50)

各プロジェクトは reviewIntervallastReviewDate も返します。

omnifocus_mark_project_reviewed

プロジェクトをレビュー済みとしてマークし、次のレビュー日を更新します。プロジェクトはIDまたは名前のどちらでも特定できます。

{
  "projectId": "abc123"
}

またはプロジェクト名を使用する場合:

{
  "projectName": "Weekly Review"
}

カスタムのレビュー間隔を指定する場合:

{
  "projectName": "Work Project",
  "reviewIntervalDays": 14
}

パラメータ:

  • projectId または projectName: プロジェクトを特定します(IDが優先)

  • reviewIntervalDays(オプション): カスタムのレビュー間隔(日数)。指定しない場合は、プロジェクトの既存のレビュー間隔が使用されます。

omnifocus_batch_mark_reviewed

複数のプロジェクトを1回の効率的な操作でレビュー済みとしてマークします。

{
  "projectIds": ["id1", "id2", "id3"]
}

すべてにカスタムのレビュー間隔を指定する場合:

{
  "projectIds": ["id1", "id2", "id3"],
  "reviewIntervalDays": 7
}

パラメータ:

  • projectIds: レビュー済みとしてマークするプロジェクトIDの配列(1〜100プロジェクト)

  • reviewIntervalDays(オプション): すべてのプロジェクトに適用するカスタムのレビュー間隔

次の内容を含むサマリーを返します:

  • 成功したレビューの数

  • 失敗の数

  • 成功したレビューの完全なプロジェクトデータ

  • 失敗のエラー詳細

日付形式

すべての日付はISO 8601形式 YYYY-MM-DDTHH:mm:ss を使用します。

例:

  • 2024-12-31T17:00:00 - 2024年12月31日 午後5:00

  • 2024-06-15T09:00:00 - 2024年6月15日 午前9:00

エラーハンドリング

サーバーは一般的な問題に対して明確なエラーメッセージを提供します:

  • OmniFocusが実行されていません: 最初にOmniFocusを起動してください

  • 権限が拒否されました: システム環境設定で自動化の権限を有効にしてください

  • 項目が見つかりません: 指定されたIDが存在しません

  • パラメータが無効です: パラメータの形式と値を確認してください

開発

ビルド

npm run build

ウォッチモード

npm run dev

テスト

npm test              # All unit tests
npm run test:watch    # Watch mode
npm run test:coverage # Coverage report (thresholds enforced: 80% lines, 75% branches)

src/__tests__/integration.test.ts の統合テストはデフォルトではスキップされます。これらのテストは、実行中のOmniFocusが必要であり、実際のデータベースを変更するためです。

手動テスト

ビルド後、以下でテストできます:

echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js

実行中のサーバーへの変更の適用(重要)

MCPクライアントはツールリストを接続時に一度だけ取得し、セッション中はそれをキャッシュします。npm run build だけでは、すでに接続されているクライアントは更新されません。Nodeはホットリロードを行わず、クライアントはスキーマを再取得しないためです。ツールやスキーマを変更した後は、サーバーを再起動し、各クライアントに再接続させる必要があります:

  1. 再ビルド: npm run build

  2. サーバープロセスを再起動して、新しい dist/ を読み込ませます:

    • LaunchAgent(HTTPトランスポート): launchctl kickstart -k gui/$(id -u)/com.sanderrobijns.omnifocus-mcp

    • 新しいスキーマを提供していることを確認するには: lsof -nP -iTCP:3000 -sTCP:LISTEN で、新しく開始されたPIDが表示されるはずです。

  3. 各クライアントを再接続して、tools/list を再取得させます:

    • Claude Code / Cowork: 新しいセッションを開始します(実行中のセッションは、その存続期間中キャッシュされたスキーマを保持します)。

    • Claude Desktop: アプリを終了して再度開きます(またはサーバーをオフ/オンに切り替えます)。

    • claude.ai / Claude iOS カスタムコネクタ: 設定 → コネクタでコネクタを再同期します(ツールリストはコネクタレベルでキャッシュされます)。

クライアントが再接続するまで、サーバーがすでに新しいスキーマを提供していても、クライアントには古いスキーマが表示され続けます。

ライセンス

MIT

クレジット

以下を使用して構築されています:

Install Server
A
license - permissive license
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity
Issues opened vs closed

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…

  • Personal assistant MCP server with search, execute, packages, jobs, secrets, and integrations.

  • MCP connector that lets ChatGPT list, search, and run your Apple Shortcuts via a local Mac agent

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/estrenuo/omnifocus-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server