Koskamo

toolkit-mcp-server

An open-source MCP server providing developer utilities including cryptographic hashing, ID generation, QR code creation, encoding/decoding, and IP geolocation. It also offers optional gated network and system diagnostics for server host information.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

toolkit-mcp-server logo, by cyanheadsCommunity listingStreamable HTTPNo sign-inEndpoint checked 2026-11-07Published by cyanheads
MCP endpoint (Streamable HTTP)
https://toolkit.caseyjhand.com/mcp
Vendor documentation

What its tools do

5 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

5 of 5 have a description · 5 of 5 declare behavior hints · 5 of 5 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Generate cryptographic digests or HMACs for data integrity verification
  • Mint random UUIDs or ULIDs for unique identifier generation
  • Encode text or URLs into QR codes in various formats
  • Geolocate public IP addresses to retrieve country and ASN details

Source and maintenance

Community listingRegistry 2.4.0
Maintenance checked

Protocol and capabilities

Server
toolkit-mcp-server 2.4.0
MCP protocol negotiated in check
2025-11-25
Tools
5 advertised
Resources
1 advertised
Resource templates
1 advertised
Prompts
0 advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  },
  "logging": {},
  "prompts": {
    "listChanged": true
  },
  "resources": {
    "subscribe": true,
    "listChanged": true
  },
  "extensions": {
    "io.modelcontextprotocol/skills": {
      "directoryRead": true
    }
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Five tools are always available: toolkit_hash_value (digests and HMACs, and constant-time comparison against a published checksum or signature), toolkit_encode_value (base64, base64url, hex, and URL encoding), toolkit_generate_id (UUIDs and ULIDs from the platform CSPRNG), toolkit_generate_qr (QR codes), and toolkit_geolocate_ip. Geolocation is the only one that calls an outside provider, and its per-minute request budgets belong to this server process and are shared by every caller: send several addresses as one batch and reuse results already returned. The tools work on the strings you pass; none reads a file path or fetches a URL, so a path sent as a value is processed as text. Two host diagnostics, toolkit_check_network and toolkit_check_system, register only when the operator starts the server with TOOLKIT_ENABLE_NET_DIAGNOSTICS=true or TOOLKIT_ENABLE_SYSTEM_INFO=true, and they report on the machine this server runs on, not the caller's. The toolkit-guide skill (skill://toolkit-guide/SKILL.md) carries the gates for reading a digest or signature mismatch, webhook and JWT signing inputs, moving digests between encodings, who runs a network, and host diagnostics.

Tools

5 advertised

toolkit_hash_value — toolkit-mcp-server: hash value
Advertised by the server during the last successful anonymous check.
Advertised read-only

Generate a cryptographic digest or HMAC of a value, or verify a value against an expected digest. Set operation to "generate" for a digest, or "compare" to constant-time-check value against the expected digest — compare is timing-safe and avoids manual string equality checks. Omitting operation compares when expected is supplied and generates otherwise. Algorithm defaults to sha256; sha384 and sha512 are also secure, while md5 and sha1 are exposed ONLY for checksum and file-integrity compatibility and for verifying existing HMAC-SHA1 signatures, and must not be used for passwords, new signatures, or any other security purpose. Set secret to compute HMAC(algorithm, secret, value) instead of a plain digest, for generate and compare alike — the check behind webhook signatures such as GitHub's X-Hub-Signature-256 (pass the header value as expected) and HS256 JWT signatures; secretEncoding says how secret is written (utf8 default, hex, base64, or base64url for a JWK k), the result carries hmac: true, and the secret is never echoed back. digestEncoding selects the generated digest form: lowercase hex (default), base64, base64url (unpadded, the JWS signature form), or sri (<algorithm>-<base64>, the npm lockfile integrity and Subresource Integrity form, sha256/sha384/sha512 only, plain digests only). expected is recognized by its shape at the algorithm's digest length, so a published checksum can be pasted as-is: a bare hex, base64, or base64url digest; SRI, which may hold several space-separated entries as an npm integrity field can and matches when any entry for algorithm does; one checksum-file line as sha256sum, shasum, md5, or openssl dgst prints it (<hex> <file>, or SHA256 (<file>) = <digest>); or a digest prefixed sha256: or sha256= (OCI and release-asset digests, webhook signature headers). A tag or prefix naming another algorithm fails rather than being ignored, and a multi-line checksum file must be cut to the one line for the file. inputEncoding controls how value is read before hashing (utf8 default, or hex/base64 for raw binary data) so binary blobs need no decode round-trip. The canonical use is matching a download against a vendor-published checksum or a lockfile integrity entry.

toolkit_hash_value input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "value"
  ],
  "properties": {
    "value": {
      "type": "string",
      "description": "The data to hash, interpreted per inputEncoding (raw text by default)."
    },
    "secret": {
      "type": "string",
      "description": "HMAC key. When non-empty, the digest is HMAC(algorithm, secret, value) instead of a plain hash, for generate and compare alike. Read per secretEncoding and used exactly as sent, never trimmed; an empty string counts as absent. Never echoed in the result or in error messages."
    },
    "expected": {
      "type": "string",
      "description": "The digest to compare against, recognized from its shape at the algorithm's digest length: hex (any case), standard base64, or base64url, bare or prefixed as <algorithm>:<digest> or <algorithm>=<digest>; SRI (<algorithm>-<base64>); or one checksum-file line, <hex>  <file> (sha256sum, shasum, md5 -r) or SHA256 (<file>) = <digest> (BSD, --tag, openssl dgst). A string of only hex digits is always read as hex; hex split by whitespace is rejected, as is a <hex>  <file> line whose file name is only hex digits. An SRI value may carry several space-separated entries: entries for other algorithms are skipped, and it matches when any entry for algorithm matches. A tag or prefix naming another algorithm is rejected, as is a whole multi-line checksum file. Supplying it with operation omitted runs a compare; it is rejected with operation \"generate\"."
    },
    "algorithm": {
      "enum": [
        "sha256",
        "sha384",
        "sha512",
        "sha1",
        "md5"
      ],
      "type": "string",
      "default": "sha256",
      "description": "Digest algorithm. sha256 (default), sha384, or sha512 for security; md5/sha1 are for checksums and for verifying existing HMAC-SHA1 signatures only — not for new security use."
    },
    "operation": {
      "enum": [
        "generate",
        "compare"
      ],
      "type": "string",
      "description": "\"generate\" produces a digest; \"compare\" constant-time-checks value against expected. When omitted, resolves to \"compare\" if expected is supplied and \"generate\" otherwise."
    },
    "inputEncoding": {
      "enum": [
        "utf8",
        "hex",
        "base64"
      ],
      "type": "string",
      "default": "utf8",
      "description": "How value is decoded before hashing: utf8 text, hex, or base64."
    },
    "digestEncoding": {
      "enum": [
        "hex",
        "base64",
        "base64url",
        "sri"
      ],
      "type": "string",
      "default": "hex",
      "description": "Form of the generated digest: lowercase hex (default), standard base64, base64url (URL-safe alphabet, unpadded — the JWS signature form), or sri (<algorithm>-<base64>, sha256/sha384/sha512 only, and not with secret). Applies to operation \"generate\"."
    },
    "secretEncoding": {
      "enum": [
        "utf8",
        "hex",
        "base64",
        "base64url"
      ],
      "type": "string",
      "default": "utf8",
      "description": "How secret is written: utf8 text (default, as a webhook signing secret is), hex, base64, or base64url (as a JWK k member is). Read only when secret is non-empty."
    }
  },
  "additionalProperties": false
}
toolkit_hash_value output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "algorithm",
        "operation"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "hmac": {
      "type": "boolean",
      "const": true,
      "description": "Present (true) when the digest is an HMAC keyed by secret; absent for a plain digest."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "missing_expected",
                "expected_without_compare",
                "expected_malformed",
                "expected_length_mismatch",
                "expected_algorithm_mismatch",
                "sri_unsupported_algorithm",
                "invalid_input_encoding",
                "invalid_secret_encoding",
                "sri_with_secret"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `missing_expected`: operation is \"compare\" but no expected digest was supplied. `expected_without_compare`: operation is \"generate\" but an expected digest was also supplied, so it would be ignored. `expected_malformed`: expected is not a digest in any form the tool reads, spans several lines (a whole checksum file), or is an SRI value holding a token that is not an SRI entry. `expected_length_mismatch`: expected is a recognized digest form but its length does not match the algorithm, so compare would always fail. `expected_algorithm_mismatch`: expected names an algorithm other than the chosen one, in a checksum-line tag, an <algorithm>: or <algorithm>= prefix, or SRI entries none of which names the chosen algorithm. `sri_unsupported_algorithm`: digestEncoding is \"sri\" and algorithm is md5 or sha1, which SRI does not define. `invalid_input_encoding`: value is not valid for the declared inputEncoding (e.g. non-hex characters with inputEncoding \"hex\"). `invalid_secret_encoding`: secret is not valid for the declared secretEncoding (e.g. non-hex characters with secretEncoding \"hex\"). `sri_with_secret`: digestEncoding is \"sri\" on operation \"generate\" and a secret was supplied; SRI carries plain digests, not HMACs. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "digest": {
      "type": "string",
      "description": "Digest of value in the requested digestEncoding: lowercase hex, base64, unpadded base64url, or <algorithm>-<base64>; the HMAC when hmac is true. Present for operation \"generate\"."
    },
    "matches": {
      "type": "boolean",
      "description": "Constant-time equality of the computed digest (the HMAC when hmac is true) against expected. Present for operation \"compare\"."
    },
    "algorithm": {
      "enum": [
        "sha256",
        "sha384",
        "sha512",
        "sha1",
        "md5"
      ],
      "type": "string",
      "description": "The algorithm used."
    },
    "operation": {
      "enum": [
        "generate",
        "compare"
      ],
      "type": "string",
      "description": "The operation performed, after resolving an omitted operation."
    },
    "lengthInBytes": {
      "type": "number",
      "description": "Digest size in bytes (32 for sha256, 48 for sha384, 64 for sha512, 20 for sha1, 16 for md5). Present for \"generate\"."
    }
  },
  "additionalProperties": false
}
toolkit_hash_value annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true
}
toolkit_generate_id — toolkit-mcp-server: generate id
Advertised by the server during the last successful anonymous check.
Advertised read-only

Mint cryptographically-random identifiers using the platform CSPRNG — the correct source for IDs that must be unpredictable, unlike model-generated values. type selects the format: uuid_v4 (random, the default), uuid_v7 (time-ordered, sortable by creation), or ulid (26-char Crockford-base32, lexicographically sortable). Set count to mint a batch in one call (up to 1000); the returned ids array always contains exactly count values and is never truncated. For uuid_v7 and ulid, a batch is monotonic — strictly increasing even within the same millisecond — so the ids array stays in sorted creation order; ids minted in the same millisecond are separated by random gaps, so no id in a batch can be derived from another. IDs from this tool feed into toolkit_generate_qr (pass ids[0] as data) to create a scannable code.

toolkit_generate_id input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "type": {
      "enum": [
        "uuid_v4",
        "uuid_v7",
        "ulid"
      ],
      "type": "string",
      "default": "uuid_v4",
      "description": "Identifier format: uuid_v4 (random), uuid_v7 (time-ordered), or ulid (sortable Crockford-base32)."
    },
    "count": {
      "type": "integer",
      "default": 1,
      "maximum": 1000,
      "minimum": 1,
      "description": "How many identifiers to mint (1–1000). The full batch is always returned."
    }
  },
  "additionalProperties": false
}
toolkit_generate_id output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "type",
        "ids",
        "count"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "ids": {
      "type": "array",
      "items": {
        "type": "string",
        "description": "A single minted identifier."
      },
      "description": "The minted identifiers — exactly count of them, in mint order; for uuid_v7 and ulid that order is strictly increasing (sorted by creation), with random gaps between ids minted in the same millisecond."
    },
    "type": {
      "enum": [
        "uuid_v4",
        "uuid_v7",
        "ulid"
      ],
      "type": "string",
      "description": "The identifier format that was minted."
    },
    "count": {
      "type": "number",
      "description": "The number of identifiers minted (equals the requested count)."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "description": "Machine-readable failure mode."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    }
  },
  "additionalProperties": false
}
toolkit_generate_id annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": false
}
toolkit_generate_qr — toolkit-mcp-server: generate QR code
Advertised by the server during the last successful anonymous check.
Advertised read-only

Encode text or a URL into a QR code. data is the content to encode (a link, a generated identifier such as toolkit_generate_id's ids[0], or any string). format selects the output: svg returns inline SVG markup sized in pixels, png_base64 returns base64-encoded PNG bytes (with mimeType and byteLength), and terminal returns plain Unicode half-block characters (no escape codes) for a monospace display, drawn for a dark background: light modules, quiet zone included, are blocks and dark modules are spaces. errorCorrection (L/M/Q/H) trades data capacity for damage tolerance, margin sets the quiet-zone width in modules, and scale sets pixels per module for svg and png_base64, so both are (modules + 2 × margin) × scale pixels per side. The returned version (1–40) reflects how dense the encoded data is. png_base64 rejects an image past 2048 px per side with a typed raster_too_large error, so a dense symbol needs a lower scale; svg is vector markup and carries no such limit.

toolkit_generate_qr input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "data"
  ],
  "properties": {
    "data": {
      "type": "string",
      "maxLength": 2953,
      "minLength": 1,
      "description": "The text or URL to encode, stored as UTF-8. Capacity is counted in bytes: 2953 UTF-8 bytes is the absolute ceiling (QR version 40, level L, byte mode). The 2953-character limit here is only an upper bound, since a non-ASCII character takes 2–4 bytes. Usable capacity drops at higher errorCorrection levels, so over-capacity data is rejected with a typed data_too_large error rather than a generic failure."
    },
    "scale": {
      "type": "integer",
      "default": 4,
      "maximum": 32,
      "minimum": 1,
      "description": "Pixels per module for svg (its width and height) and png_base64. Ignored for terminal. png_base64 also bounds the whole image at 2048 px per side, so a dense symbol or a wide margin admits a lower scale than 32 there."
    },
    "format": {
      "enum": [
        "svg",
        "png_base64",
        "terminal"
      ],
      "type": "string",
      "default": "svg",
      "description": "Output format: svg markup, png_base64 (raster bytes), or terminal (plain Unicode half-blocks, drawn for a dark background)."
    },
    "margin": {
      "type": "integer",
      "default": 4,
      "maximum": 20,
      "minimum": 0,
      "description": "Quiet-zone width in modules around the symbol. The spec recommends 4."
    },
    "errorCorrection": {
      "enum": [
        "L",
        "M",
        "Q",
        "H"
      ],
      "type": "string",
      "default": "M",
      "description": "Error-correction level: L (~7% recoverable) to H (~30%). Higher tolerance lowers data capacity."
    }
  },
  "additionalProperties": false
}
toolkit_generate_qr output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "format",
        "content",
        "version"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "data_too_large",
                "raster_too_large"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `data_too_large`: data exceeds the QR capacity for the chosen errorCorrection level and encoding mode. `raster_too_large`: format is png_base64 and (modules + 2 × margin) × scale exceeds the pixel budget. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "format": {
      "enum": [
        "svg",
        "png_base64",
        "terminal"
      ],
      "type": "string",
      "description": "The format that was produced."
    },
    "content": {
      "type": "string",
      "description": "The QR artifact: SVG markup, the terminal half-block grid (newline-separated rows), or base64 PNG bytes for png_base64."
    },
    "version": {
      "type": "number",
      "description": "QR symbol version (1–40); higher versions hold denser data and indicate denser content."
    },
    "mimeType": {
      "enum": [
        "image/svg+xml",
        "image/png"
      ],
      "type": "string",
      "description": "MIME type of content for image formats. Absent for the terminal format."
    },
    "byteLength": {
      "type": "number",
      "description": "Decoded byte size of the PNG. Present only for png_base64."
    }
  },
  "additionalProperties": false
}
toolkit_generate_qr annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true
}
toolkit_encode_value — toolkit-mcp-server: encode value
Advertised by the server during the last successful anonymous check.
Advertised read-only

Encode or decode a value across base64, base64url, hex, or URL (percent) encoding, in either direction. Set operation to "encode" to transform raw UTF-8 text into the chosen encoding, or "decode" to recover the original bytes from an encoded value. Decoded bytes come back as UTF-8 text by default; set outputEncoding to "hex", "base64", or "base64url" (unpadded, the form JWK members, JWS signatures, and PKCE code challenges use) to receive them re-encoded instead, which is lossless for binary data and transcodes between encodings (a base64 digest to hex, or hex key bytes to base64url, for example). Decoding never substitutes replacement characters: bytes that are not valid UTF-8 text are reported as a recoverable error that points at outputEncoding. Whitespace in hex, base64, and base64url values is ignored, so line-wrapped MIME bodies and PEM bodies decode as-is (drop PEM's -----BEGIN/END----- lines, which are not base64). base64url uses the URL-safe alphabet (- and _ instead of + and /); url applies encodeURIComponent and percent-decoding. A value that is malformed for the chosen encoding is reported as a recoverable error, not a silent best-effort.

toolkit_encode_value input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "operation",
    "encoding",
    "value"
  ],
  "properties": {
    "value": {
      "type": "string",
      "description": "The value to transform — raw text for encode, an encoded string for decode. Whitespace is ignored when decoding hex, base64, or base64url; a url value is taken literally."
    },
    "encoding": {
      "enum": [
        "base64",
        "base64url",
        "hex",
        "url"
      ],
      "type": "string",
      "description": "The encoding to apply: base64, URL-safe base64url, hex, or URL percent-encoding."
    },
    "operation": {
      "enum": [
        "encode",
        "decode"
      ],
      "type": "string",
      "description": "\"encode\" transforms text into the encoding; \"decode\" recovers the bytes from an encoded value."
    },
    "outputEncoding": {
      "enum": [
        "utf8",
        "hex",
        "base64",
        "base64url"
      ],
      "type": "string",
      "description": "Decode only: how the recovered bytes are returned. utf8 (used when omitted) returns text and fails when the bytes are not valid UTF-8; hex, base64, and base64url (unpadded) return the raw bytes re-encoded, losslessly. Rejected when operation is \"encode\"."
    }
  },
  "additionalProperties": false
}
toolkit_encode_value output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "encoding",
        "operation",
        "result"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "decode_failed",
                "decode_not_utf8",
                "output_encoding_not_applicable"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `decode_failed`: operation is \"decode\" but value is malformed for the chosen encoding. `decode_not_utf8`: operation is \"decode\", outputEncoding is utf8 (or omitted), and the decoded bytes are not valid UTF-8 text. `output_encoding_not_applicable`: operation is \"encode\" and outputEncoding was supplied; it selects how decoded bytes are returned. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "result": {
      "type": "string",
      "description": "The transformed value: encoded text for encode; for decode, the recovered bytes as UTF-8 text, hex, base64, or unpadded base64url per outputEncoding."
    },
    "encoding": {
      "enum": [
        "base64",
        "base64url",
        "hex",
        "url"
      ],
      "type": "string",
      "description": "The encoding that was applied."
    },
    "operation": {
      "enum": [
        "encode",
        "decode"
      ],
      "type": "string",
      "description": "The operation that was performed."
    },
    "outputEncoding": {
      "enum": [
        "utf8",
        "hex",
        "base64",
        "base64url"
      ],
      "type": "string",
      "description": "How result renders the decoded bytes: utf8 text, hex, base64, or unpadded base64url. Present for operation \"decode\"."
    }
  },
  "additionalProperties": false
}
toolkit_encode_value annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true
}
toolkit_geolocate_ip — toolkit-mcp-server: geolocate IP
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Resolve a public IP or dotted hostname to country, region, city, coordinates, ASN, AS holder and name, organization, ISP, timezone, and proxy/hosting/mobile flags. Supply target for one lookup or targets for an ordered batch, never both; blank target and empty targets are unset. Batches process the first 100 positions (duplicates retained) and disclose every remainder in unprocessedTargets; at most 1000 inputs are accepted. Each batch position reports its result or an error with recovery, so cached and local outcomes survive quota refusals. The provider is called directly, never the target. Hostnames are DNS-resolved and resolvedIp names the located address. Private/reserved addresses have no public geolocation. Results are best-effort, city-level at best; VPNs, proxies, mobile NAT, and anycast defeat IP-to-location. Read proxy, hosting, and mobile before trusting coordinates: true describes infrastructure, not the user. org can name a customer or sub-allocation rather than the network operator; read isp, asHolder, and asName for the operator. Absent metadata remains unknown. Provider-supplied names are third-party registry text; content[] escapes them so they read as literal text, never as Markdown. source names the provider. Single and batch quotas are independent; cache hits consume neither quota.

toolkit_geolocate_ip input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "target": {
      "anyOf": [
        {
          "type": "string",
          "pattern": "^\\s*$",
          "description": "Blank means unset."
        },
        {
          "type": "string",
          "format": "ipv4",
          "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$",
          "description": "A raw IPv4 address."
        },
        {
          "type": "string",
          "format": "ipv6",
          "pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$",
          "description": "A raw IPv6 address."
        },
        {
          "type": "string",
          "pattern": "^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$",
          "description": "A dotted hostname, e.g. \"example.com\"."
        }
      ],
      "description": "One public IP or dotted hostname; mutually exclusive with nonempty targets."
    },
    "targets": {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "type": "string",
            "format": "ipv4",
            "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$",
            "description": "A raw IPv4 address."
          },
          {
            "type": "string",
            "format": "ipv6",
            "pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$",
            "description": "A raw IPv6 address."
          },
          {
            "type": "string",
            "pattern": "^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$",
            "description": "A dotted hostname, e.g. \"example.com\"."
          }
        ],
        "description": "A public IPv4/IPv6 address or a hostname (e.g. \"8.8.8.8\" or \"example.com\")."
      },
      "maxItems": 1000,
      "description": "Ordered public IPs or dotted hostnames; first 100 positions are served, every remainder is disclosed. Mutually exclusive with nonblank target."
    }
  },
  "additionalProperties": false
}
toolkit_geolocate_ip output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      }
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "asn": {
      "type": "string",
      "maxLength": 256,
      "description": "Autonomous System number, e.g. \"AS15169\". Absent on providers that omit it."
    },
    "isp": {
      "type": "string",
      "maxLength": 256,
      "description": "Internet service provider for the address, e.g. \"Cloudflare, Inc\". Absent when unknown."
    },
    "org": {
      "type": "string",
      "maxLength": 256,
      "description": "Organization the address is registered to, e.g. \"Google LLC\"; often a customer or sub-allocation label rather than the network operator. Carries asHolder, else isp, when the provider names no organization. Absent when all three are unknown."
    },
    "city": {
      "type": "string",
      "maxLength": 256,
      "description": "City name. Absent when unknown."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "rate_limited",
                "unresolvable_host",
                "private_target"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: The endpoint request quota or provider cooldown is exhausted. `unresolvable_host`: A hostname target failed DNS resolution. `private_target`: The target resolves to a private/reserved IP, or to a public-format IP the provider reports as unlocatable (reserved, unrouted, or bogon); neither has public geolocation. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "proxy": {
      "type": "boolean",
      "description": "True when the address is a known proxy, VPN, or Tor exit — the location describes the exit node, not the user. Absent when the provider does not report it."
    },
    "asName": {
      "type": "string",
      "maxLength": 256,
      "description": "Registry name of the autonomous system, e.g. \"CLOUDFLARENET\". Absent when the provider omits it."
    },
    "mobile": {
      "type": "boolean",
      "description": "True when the address belongs to a mobile carrier network, where NAT can place the location far from the device. Absent when unreported."
    },
    "region": {
      "type": "string",
      "maxLength": 256,
      "description": "Region or state name. Absent when unknown."
    },
    "source": {
      "type": "string",
      "description": "The provider that answered the lookup, e.g. \"ip-api\"."
    },
    "target": {
      "type": "string",
      "description": "The target as supplied (IP or hostname)."
    },
    "country": {
      "type": "string",
      "maxLength": 256,
      "description": "Country name. Absent when the provider does not report it."
    },
    "hosting": {
      "type": "boolean",
      "description": "True when the address belongs to a hosting or datacenter network, so the location is a facility rather than a person. Absent when unreported."
    },
    "results": {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "type": "object",
            "required": [
              "target",
              "resolvedIp",
              "source"
            ],
            "properties": {
              "asn": {
                "type": "string",
                "maxLength": 256,
                "description": "Autonomous System number, e.g. \"AS15169\". Absent on providers that omit it."
              },
              "isp": {
                "type": "string",
                "maxLength": 256,
                "description": "Internet service provider for the address, e.g. \"Cloudflare, Inc\". Absent when unknown."
              },
              "org": {
                "type": "string",
                "maxLength": 256,
                "description": "Organization the address is registered to, e.g. \"Google LLC\"; often a customer or sub-allocation label rather than the network operator. Carries asHolder, else isp, when the provider names no organization. Absent when all three are unknown."
              },
              "city": {
                "type": "string",
                "maxLength": 256,
                "description": "City name. Absent when unknown."
              },
              "proxy": {
                "type": "boolean",
                "description": "True when the address is a known proxy, VPN, or Tor exit — the location describes the exit node, not the user. Absent when the provider does not report it."
              },
              "asName": {
                "type": "string",
                "maxLength": 256,
                "description": "Registry name of the autonomous system, e.g. \"CLOUDFLARENET\". Absent when the provider omits it."
              },
              "mobile": {
                "type": "boolean",
                "description": "True when the address belongs to a mobile carrier network, where NAT can place the location far from the device. Absent when unreported."
              },
              "region": {
                "type": "string",
                "maxLength": 256,
                "description": "Region or state name. Absent when unknown."
              },
              "source": {
                "type": "string",
                "description": "The provider that answered the lookup, e.g. \"ip-api\"."
              },
              "target": {
                "type": "string",
                "description": "The target as supplied (IP or hostname)."
              },
              "country": {
                "type": "string",
                "maxLength": 256,
                "description": "Country name. Absent when the provider does not report it."
              },
              "hosting": {
                "type": "boolean",
                "description": "True when the address belongs to a hosting or datacenter network, so the location is a facility rather than a person. Absent when unreported."
              },
              "asHolder": {
                "type": "string",
                "maxLength": 256,
                "description": "Holder of the autonomous system as the provider names it after the ASN, e.g. \"Cloudflare, Inc.\" for AS13335. Absent when the provider reports no AS or no holder name."
              },
              "latitude": {
                "type": "number",
                "description": "Latitude in decimal degrees. Absent when unknown."
              },
              "timezone": {
                "type": "string",
                "maxLength": 256,
                "description": "IANA timezone, e.g. \"America/Los_Angeles\". Absent when unknown."
              },
              "longitude": {
                "type": "number",
                "description": "Longitude in decimal degrees. Absent when unknown."
              },
              "resolvedIp": {
                "type": "string",
                "description": "The IP that was actually located (a supplied hostname is resolved to this first)."
              },
              "countryCode": {
                "type": "string",
                "maxLength": 256,
                "description": "ISO 3166-1 alpha-2 country code. Absent when unknown."
              }
            },
            "description": "Normalized geolocation metadata for one target.",
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "target",
              "error"
            ],
            "properties": {
              "error": {
                "type": "object",
                "required": [
                  "reason",
                  "message",
                  "recovery"
                ],
                "properties": {
                  "reason": {
                    "enum": [
                      "unresolvable_host",
                      "private_target",
                      "rate_limited",
                      "provider_unavailable"
                    ],
                    "type": "string",
                    "description": "Why this target could not be located."
                  },
                  "message": {
                    "type": "string",
                    "description": "Server-authored failure message."
                  },
                  "recovery": {
                    "type": "object",
                    "required": [
                      "hint"
                    ],
                    "properties": {
                      "hint": {
                        "type": "string",
                        "description": "How to recover this target lookup."
                      }
                    },
                    "description": "Recovery guidance for the failed target.",
                    "additionalProperties": false
                  },
                  "waitTimeSeconds": {
                    "type": "number",
                    "description": "Seconds to wait before retrying a rate-limited target."
                  }
                },
                "description": "Failure for this input position.",
                "additionalProperties": false
              },
              "target": {
                "type": "string",
                "description": "The failed target as supplied."
              }
            },
            "description": "A failed batch target with recovery guidance.",
            "additionalProperties": false
          }
        ],
        "description": "Result or failure for one input position."
      },
      "description": "Ordered results or failures for the first 100 input positions."
    },
    "asHolder": {
      "type": "string",
      "maxLength": 256,
      "description": "Holder of the autonomous system as the provider names it after the ASN, e.g. \"Cloudflare, Inc.\" for AS13335. Absent when the provider reports no AS or no holder name."
    },
    "latitude": {
      "type": "number",
      "description": "Latitude in decimal degrees. Absent when unknown."
    },
    "timezone": {
      "type": "string",
      "maxLength": 256,
      "description": "IANA timezone, e.g. \"America/Los_Angeles\". Absent when unknown."
    },
    "longitude": {
      "type": "number",
      "description": "Longitude in decimal degrees. Absent when unknown."
    },
    "resolvedIp": {
      "type": "string",
      "description": "The IP that was actually located (a supplied hostname is resolved to this first)."
    },
    "countryCode": {
      "type": "string",
      "maxLength": 256,
      "description": "ISO 3166-1 alpha-2 country code. Absent when unknown."
    },
    "failedCount": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": -9007199254740991,
      "description": "Number of failed processed targets."
    },
    "unprocessedTargets": {
      "type": "array",
      "items": {
        "type": "string",
        "description": "One unprocessed target."
      },
      "description": "Every input position past the first 100; resubmit these targets in another batch."
    }
  },
  "additionalProperties": false
}
toolkit_geolocate_ip annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}

Prompts

0 advertised

No prompt definitions available.

Resources

1 advertised

toolkit-guide

Advertised by the server during the last successful anonymous check.

Gates for the toolkit_* tools. Use when checking a download checksum, webhook HMAC, or HS256 JWT with toolkit_hash_value; moving digest bytes between hex, base64, and base64url; or reading who runs an IP's network from toolkit_geolocate_ip. Also covers the operator-gated toolkit_check_network and toolkit_check_system. Read before reporting a mismatch, a down host, or an IP's owner, or when a task needs a host diagnostic missing from the tool list.

toolkit-guide URI
skill://toolkit-guide/SKILL.md

MIME type: text/markdown

io.modelcontextprotocol/skills

Advertised by the server during the last successful anonymous check.

io.modelcontextprotocol/skills URI template
skill://{+path}

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
591 ms
95th percentile latency
591 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 591 ms · metadata changed · 5 tool(s), 1 resource(s), 1 resource template(s), 0 prompt(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http toolkit-mcp-server https://toolkit.caseyjhand.com/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add toolkit-mcp-server --url https://toolkit.caseyjhand.com/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable toolkit-mcp-server in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "toolkit-mcp-server": {
      "url": "https://toolkit.caseyjhand.com/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "toolkit-mcp-server": {
      "type": "streamableHttp",
      "url": "https://toolkit.caseyjhand.com/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start toolkit-mcp-server and inspect its tools.
VS Code configuration
{
  "servers": {
    "toolkit-mcp-server": {
      "type": "http",
      "url": "https://toolkit.caseyjhand.com/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http toolkit-mcp-server https://toolkit.caseyjhand.com/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://toolkit.caseyjhand.com/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "toolkit-mcp-server": {
      "serverUrl": "https://toolkit.caseyjhand.com/mcp"
    }
  }
}

Let your agent find its tools

Find servers like toolkit-mcp-server from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.