Quickstart: Integrate Civic Bot

Connect your AI application to Civic Bot in under 30 minutes.

1

Initialize the Connection

MCP uses JSON-RPC 2.0 over HTTP. Start by sending an initialize request to establish the protocol version.

Request
curl -X POST http://localhost:4091/mcp/message \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "clientInfo": {
        "name": "my-app",
        "version": "1.0.0"
      }
    }
  }'
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": {
      "name": "civic-bot",
      "version": "0.1.0"
    },
    "capabilities": {
      "tools": {}
    }
  }
}
2

Discover Available Tools

List all available tools to understand what queries you can make.

Request
curl -X POST http://localhost:4091/mcp/message \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }'

Civic Bot provides 8 tools:

search_jurisdictions

Find government entities

get_jurisdiction

Get details + hierarchy

search_agencies

Find agencies

get_agency

Get agency + services

search_services

Find permits, benefits

get_service

Get service details

search_officials

Find elected officials

get_official

Get official details

3

Call a Tool

Use tools/call with the tool name and arguments.

Request: Search for services
curl -X POST http://localhost:4091/mcp/message \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "search_services",
      "arguments": {
        "query": "business license"
      }
    }
  }'
Response
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"services\":[...],\"count\":1}"
      }
    ]
  }
}

Note: The result content is JSON-encoded text. Parse result.content[0].text to get the actual data.

Code Examples

Python

import requests
import json

def call_civic_bot(tool_name, arguments):
    response = requests.post(
        "http://localhost:4091/mcp/message",
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/call",
            "params": {
                "name": tool_name,
                "arguments": arguments
            }
        }
    )
    result = response.json()
    return json.loads(result["result"]["content"][0]["text"])

# Example: Search for services
services = call_civic_bot("search_services", {"query": "license"})
print(f"Found {services['count']} services")

JavaScript / Node.js

async function callCivicBot(toolName, args) {
  const response = await fetch("http://localhost:4091/mcp/message", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "tools/call",
      params: { name: toolName, arguments: args }
    })
  });
  const result = await response.json();
  return JSON.parse(result.result.content[0].text);
}

// Example: Get jurisdiction hierarchy
const data = await callCivicBot("get_jurisdiction", { id: "vacaville-ca" });
console.log(data.hierarchy.map(j => j.name).join(" → "));

Elixir

defmodule CivicBotClient do
  def call_tool(tool_name, arguments) do
    request = %{
      jsonrpc: "2.0",
      id: 1,
      method: "tools/call",
      params: %{name: tool_name, arguments: arguments}
    }

    {:ok, response} = Req.post("http://localhost:4091/mcp/message",
      json: request
    )

    response.body["result"]["content"]
    |> List.first()
    |> Map.get("text")
    |> Jason.decode!()
  end
end

# Example: Search for agencies
CivicBotClient.call_tool("search_agencies", %{query: "police"})

Error Handling

Check for errors in two places:

1. JSON-RPC Errors

Protocol-level errors (invalid method, parse error):

{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}

2. Tool Errors

Tool execution errors (not found, invalid args):

{"result":{"content":[{"type":"text","text":"Error: Not found"}],"isError":true}}

Tip: Always check for isError: true in the result before processing the response.

Next Steps