Packages
armature_mcp_analytics
0.1.0
Community-maintained Armature analytics instrumentation for Elixir MCP servers.
Current section
Files
Jump to
Current section
Files
armature_mcp_analytics
README.md
README.md
# Armature MCP Analytics for Elixir
[](https://github.com/MarkMarine/armature_mcp_analytics/actions/workflows/ci.yml)
[](https://hex.pm/packages/armature_mcp_analytics)
[](LICENSE)
> [!IMPORTANT]
> This is an unofficial, community-maintained package created by MarkMarine.
> It is not affiliated with or endorsed by Armature.
Privacy-conscious, framework-neutral integration with Armature analytics for
Elixir MCP servers. The published package is available on
[Hex.pm](https://hex.pm/packages/armature_mcp_analytics), with API documentation
on [HexDocs](https://hexdocs.pm/armature_mcp_analytics).
The library:
- decorates MCP tool schemas with optional conversation telemetry;
- strips that telemetry before validation and handler execution;
- emits bounded, redacted schema-v1 `session_init` and `tool_call` events;
- retries transient ingest failures without breaking tool handlers; and
- provides the default-on `request_capability` tool for unmet-demand signals.
It works with JSON-style tool definitions and does not require a particular
Elixir MCP framework.
## Installation
Add `armature_mcp_analytics` to `mix.exs`:
```elixir
def deps do
[
{:armature_mcp_analytics, "~> 0.1.0"}
]
end
```
To follow the repository directly instead of Hex:
```elixir
{:armature_mcp_analytics,
github: "MarkMarine/armature_mcp_analytics",
tag: "v0.1.0"}
```
## Configuration
Set these only in the server deployment environment:
```text
ANALYTICS_INGEST_API_KEY=your-ingest-key
ANALYTICS_INGEST_URL=https://app.armature.tech/api/mcp-analytics/ingest
```
The URL above is the US endpoint. EU accounts must set:
```text
ANALYTICS_INGEST_URL=https://eu.armature.tech/api/mcp-analytics/ingest
```
Missing API keys intentionally no-op, which keeps local development simple.
Never expose the ingest key to MCP clients or browser code.
## Supervision
Add the recorder to your application's supervision tree:
```elixir
children = [
{Armature.MCP.Analytics,
api_key: System.get_env("ANALYTICS_INGEST_API_KEY"),
ingest_url: System.get_env("ANALYTICS_INGEST_URL"),
delivery: :background}
]
```
Use `:background` for long-lived OTP applications and call
`Armature.MCP.Analytics.flush/0` during an orderly shutdown when practical.
Use `delivery: :await` for short-lived commands and serverless request
handlers.
## Instrument a dispatcher
Decorate the tool list before returning `tools/list`:
```elixir
tools = Armature.MCP.Analytics.decorate_tools(tools)
```
This adds the `request_capability` tool by default. Disable it with:
```elixir
tools = Armature.MCP.Analytics.decorate_tools(tools, request_capability: false)
```
Wrap normal tool dispatch so the handler receives its original arguments:
```elixir
context = %{
actor_seed: authenticated_subject,
session_id: mcp_session_id,
client_info: client_info,
client_capabilities: client_capabilities,
protocol_version: protocol_version,
request_meta: request_meta
}
Armature.MCP.Analytics.track(tool_name, raw_arguments, context, fn clean_arguments ->
dispatch_tool(tool_name, clean_arguments)
end)
```
Only provide `context.request_id` when it is a genuine, globally unique
per-invocation idempotency key. Do not pass a JSON-RPC message id: those
counters are frequently reused across sessions and can cause deduplication
collisions.
If a customer tool already defines a top-level `telemetry` argument, pass its
tool definition in the context. The library will preserve the native field:
```elixir
context = Map.put(context, :tool_definition, tool_definition)
```
Route the helper tool with:
```elixir
Armature.MCP.Analytics.MCP.handle_request_capability(arguments)
```
## Privacy and failure behavior
Before serialization, the library:
- redacts common credential fields and high-confidence token patterns;
- removes large base64 payloads;
- bounds previews, metadata, and total sanitization work; and
- hashes actor identifiers before transmission.
You can mutate or drop a whole event with `:redact_event`:
```elixir
{Armature.MCP.Analytics,
redact_event: fn event ->
update_in(event, ["metadata"], &Map.drop(&1, ["request_meta"]))
end}
```
Delivery failures go to `:on_error` and never replace tool results:
```elixir
{Armature.MCP.Analytics,
on_error: fn error ->
Logger.warning("Analytics delivery failed",
code: error.code,
status: error.status
)
end}
```
Do not log the batch, API key, or raw HTTP response in an error callback.
## Verify a live MCP server
After deployment, run Armature's content-free doctor with the same regional
environment variables:
```bash
npx @armature-tech/mcp-analytics doctor --url https://your-server.example/mcp
```
The doctor checks the MCP handshake, served tool schemas, and ingest
authentication without sending customer content.
## Development
```bash
mix deps.get
mix check
```
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution workflow.
## Releasing
Releases are created from GitHub Actions with the **Publish release** workflow:
1. Update `@version` in `mix.exs` and document the release in `CHANGELOG.md`.
2. Commit the changes to `main` and ensure CI passes.
3. Configure the repository secret `HEX_API_KEY` with permission to publish
`armature_mcp_analytics`.
4. Run **Actions → Publish release → Run workflow** and enter the exact version.
The workflow verifies the requested version matches `mix.exs`, runs the full
check and coverage gates, publishes the versioned Hex package if it does not
already exist, and creates the matching `vVERSION` GitHub release. Reruns are
safe when the Hex version or GitHub release already exists.
## License
MIT. See [LICENSE](LICENSE).