# Cotlib

> cotlib is a secure, high-performance Go library for parsing, validating, and generating Cursor-on-Target (CoT) XML messages. It features a comprehensive, embedded type catalog with metadata and XSD catalogue, robust validation logic, and LLM/AI-friendly search APIs. Designed for reliability, composability, and security.

- **Type:** MCP server
- **Install:** `agentstack add mcp-nervsystems-cotlib`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [NERVsystems](https://agentstack.voostack.com/s/nervsystems)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [NERVsystems](https://github.com/NERVsystems)
- **Source:** https://github.com/NERVsystems/cotlib

## Install

```sh
agentstack add mcp-nervsystems-cotlib
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

'…we want the target dead or saved…we gotta get away from platform centric thinking…and we gotta focus on this thing where the sum of the wisdom is a cursor over the target…and we're indifferent [to the source]'  — Gen. John Jumper

# CoT Library

[](https://goreportcard.com/report/github.com/NERVsystems/cotlib) [](https://github.com/NERVsystems/cotlib/actions/workflows/ci.yml)

A comprehensive Go library for creating, validating, and working with Cursor-on-Target (CoT) events.

## Features

- **High-performance processing**: Sub-microsecond event creation, millions of validations/sec
- Complete CoT event creation and manipulation
- XML serialization and deserialization with security protections
- Full CoT type catalog with metadata
- **Zero-allocation type lookups** and optimized memory usage
- **How and relation value support** with comprehensive validation
- Coordinate and spatial data handling
- Event relationship management
- Type validation and registration
- Secure logging with slog
- Thread-safe operations
- Detail extensions with round-trip preservation
- GeoChat message and receipt support
- Predicate-based event classification
- Security-first design
- Wildcard pattern support for types
- Type search by description or full name

## Installation

```bash
go get github.com/NERVsystems/cotlib
```
**Note:** Schema validation relies on the `libxml2` library and requires CGO to be enabled when building.
See [MIGRATION.md](MIGRATION.md) for guidance when upgrading from older versions.

## Usage

### Creating and Managing CoT Events

```go
package main

import (
    "fmt"
    "log/slog"
    "os"
    "github.com/NERVsystems/cotlib"
)

func main() {
    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

    // Create a new CoT event
    event, err := cotlib.NewEvent("UNIT-123", "a-f-G", 37.422, -122.084, 0.0)
    if err != nil {
        logger.Error("Failed to create event", "error", err)
        return
    }

    // Add detail information
    event.Detail = &cotlib.Detail{
        Contact: &cotlib.Contact{
            Callsign: "ALPHA-7",
        },
        Group: &cotlib.Group{
            Name: "Team Blue",
            Role: "Infantry",
        },
    }

    // Add relationship link
    event.AddLink(&cotlib.Link{
        Uid:      "HQ-1",
        Type:     "a-f-G-U-C",
        Relation: "p-p",
    })

    // Convert to XML
    xmlData, err := event.ToXML()
    if err != nil {
        logger.Error("Failed to convert to XML", "error", err)
        return
    }

    fmt.Println(string(xmlData))
}
```
### Building Events with EventBuilder

```go
builder := cotlib.NewEventBuilder("B1", "a-f-G", 34.0, -117.0, 0).
    WithContact(&cotlib.Contact{Callsign: "ALPHA"}).
    WithGroup(&cotlib.Group{Name: "Team Blue", Role: "Infantry"}).
    WithStaleTime(time.Now().Add(10 * time.Second))
event, err := builder.Build()
if err != nil {
    log.Fatal(err)
}
_ = event
```
### Parsing CoT XML

```go
package main

import (
    "errors"
    "fmt"
    "github.com/NERVsystems/cotlib"
)

func main() {
    xmlData := `

  
  
    
    
  
`

    // Parse XML into CoT event
    event, err := cotlib.UnmarshalXMLEvent(context.Background(), []byte(xmlData))
    if err != nil {
        fmt.Printf("Error parsing XML: %v\n", err)
        return
    }

    // Access event data
    fmt.Printf("Event Type: %s\n", event.Type)
    fmt.Printf("Location: %.6f, %.6f\n", event.Point.Lat, event.Point.Lon)
    fmt.Printf("Callsign: %s\n", event.Detail.Contact.Callsign)

    // Check event predicates
    if event.Is("friend") {
        fmt.Println("This is a friendly unit")
    }

    if event.Is("ground") {
        fmt.Println("This is a ground-based entity")
    }
}
```

#### Handling Detail Extensions

CoT events often include TAK-specific extensions inside the `` element.
`cotlib` preserves many of these extensions and validates them using embedded TAKCoT schemas. These extensions go beyond canonical CoT and include elements such as:

- `__chat`
- `__chatReceipt`
- `__chatreceipt`
- `__geofence`
- `__serverdestination`
- `__video`
- `__group`
- `archive`
- `attachmentList`
- `environment`
- `fileshare`
- `precisionlocation`
- `takv`
- `track`
- `mission`
- `status`
- `shape`
- `strokecolor`
- `strokeweight`
- `fillcolor`
- `labelson`
- `uid`
- `bullseye`
- `routeInfo`
- `color`
- `hierarchy`
- `link`
- `usericon`
- `emergency`
- `height`
- `height_unit`
- `remarks`

The `remarks` extension now follows the MITRE *CoT Remarks Schema* and includes
a `` root element, enabling validation through the
`tak-details-remarks` schema.

All of these known TAK extensions are validated against embedded schemas when decoding and during event validation. Invalid XML will result in an error. Chat messages produced by TAK clients often include a `` element inside ``. `cotlib` first validates against the standard `chat` schema and automatically falls back to the TAK-specific `tak-details-__chat` schema so these messages are accepted.

Example: adding a `shape` extension with a `strokeColor` attribute:

```go
event.Detail = &cotlib.Detail{
    Shape: &cotlib.Shape{Raw: []byte(``)},
}
```

Any unknown elements are stored in `Detail.Unknown` and serialized back
verbatim.
Unknown extensions are not validated. Although cotlib enforces XML size and depth limits, the data may still contain unexpected or malicious content. Treat these elements as untrusted and validate them separately if needed.

```go
xmlData := `

  
  
    
      
    
    
  
`

evt, _ := cotlib.UnmarshalXMLEvent(context.Background(), []byte(xmlData))
out, _ := evt.ToXML()
fmt.Println(string(out)) // prints the same XML
```

The `id` attribute on `__chat` and `__chatreceipt` elements is optional.

`Chat` now exposes additional fields such as `Chatroom`, `GroupOwner`,
`SenderCallsign`, `Parent`, `MessageID` and a slice of `ChatGrp` entries
representing group membership.

### GeoChat Messaging

`cotlib` provides full support for GeoChat messages and receipts. The `Chat`
structure models the `__chat` extension including optional `` elements
and any embedded hierarchy. Incoming chat events automatically populate
`Event.Message` from the `` element. The `Marti` type holds destination
callsigns and `Remarks` exposes the message text along with the `source`, `to`,
and `time` attributes.

Chat receipts are represented by the `ChatReceipt` structure which handles both
`__chatReceipt` and TAK-specific `__chatreceipt` forms. Parsing falls back to the
TAK schemas when required so messages from ATAK and WinTAK are accepted without
extra handling.

Example of constructing and serializing a chat message:

```go
evt, _ := cotlib.NewEvent("GeoChat.UID.Room.example", "b-t-f", 0, 0, 0)
evt.Detail = &cotlib.Detail{
    Chat: &cotlib.Chat{
        ID:             "Room",
        Chatroom:       "Room",
        GroupOwner:     "false",
        SenderCallsign: "Alpha",
        ChatGrps: []cotlib.ChatGrp{
            {ID: "Room", UID0: "AlphaUID", UID1: "BravoUID"},
        },
    },
    Marti: &cotlib.Marti{Dest: []cotlib.MartiDest{{Callsign: "Bravo"}}},
    Remarks: &cotlib.Remarks{
        Source: "Example.Alpha",
        To:     "Room",
        Text:   "Hello team",
    },
}
out, _ := evt.ToXML()
```

Note: the `groupOwner` attribute is mandatory for TAK chat messages. It must be
present for schema validation to succeed when using the TAK chat format.

Delivery or read receipts can be sent by populating `Detail.ChatReceipt` with
the appropriate `Ack`, `ID`, and `MessageID` fields.

### Validator Package

The optional `validator` subpackage provides schema checks for common detail
extensions. `validator.ValidateAgainstSchema` validates XML against embedded
XSD files. `Event.Validate` automatically checks extensions such as
`__chat`, `__chatReceipt`, `__group`, `__serverdestination`, `__video`,
`attachment_list`, `usericon`, and the drawing-related details using these
schemas. All schemas in this repository's `takcot/xsd` directory are embedded
and validated, including those like `Route.xsd` that reference other files.

### Type Validation and Catalog

The library provides comprehensive type validation and catalog management:

```go
package main

import (
    "errors"
    "fmt"
    "log"
    "github.com/NERVsystems/cotlib"
)

func main() {
    // Register a custom CoT type
    if err := cotlib.RegisterCoTType("a-f-G-U-C-F"); err != nil {
        log.Fatal(err)
    }

    // Validate a CoT type
    if err := cotlib.ValidateType("a-f-G-U-C-F"); err != nil {
        if errors.Is(err, cotlib.ErrInvalidType) {
            log.Fatal(err)
        }
    }

    // Look up type metadata
    fullName, err := cotlib.GetTypeFullName("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Full name: %s\n", fullName)
    // Output: Full name: Gnd/Equip/Nbc Equipment

    // Get type description
    desc, err := cotlib.GetTypeDescription("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Description: %s\n", desc)
    // Output: Description: NBC EQUIPMENT

    // Retrieve full type information
    info, err := cotlib.GetTypeInfo("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s - %s\n", info.FullName, info.Description)
    // Output: Gnd/Equip/Nbc Equipment - NBC EQUIPMENT

    // Batch lookup for multiple types
    infos, err := cotlib.GetTypeInfoBatch([]string{"a-f-G-E-X-N", "a-f-G-U-C"})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Batch size: %d\n", len(infos))

    // Search for types by description
    types := cotlib.FindTypesByDescription("NBC")
    for _, t := range types {
        fmt.Printf("Found type: %s (%s)\n", t.Name, t.Description)
    }

    // Search for types by full name
    types = cotlib.FindTypesByFullName("Equipment")
    for _, t := range types {
        fmt.Printf("Found type: %s (%s)\n", t.Name, t.FullName)
    }
}
```

`catalog.Upsert` precomputes upper-case versions of each type's `FullName` and
`Description`. `FindByDescription` and `FindByFullName` reuse these cached
strings so searches are allocation-free.

### Type Validation

The library enforces strict validation of CoT types:
- Basic syntax checking
- Standard prefix validation
- Length limits
- Wildcard pattern validation
- Type catalog verification
- Automatic resolution of `f`, `h`, `n`, or `u` segments to catalog
  entries containing `.`

```go
// Examples of different validation scenarios:
cotlib.ValidateType("a-f-G")             // Valid - Friendly Ground
cotlib.ValidateType("b-m-r")             // Valid - Route
cotlib.ValidateType("invalid")           // Error - Unknown type
```

### How and Relation Values

The library provides full support for CoT how values (indicating position source) and relation values (for event relationships):

#### How Values

How values indicate the source or method of position determination:

```go
package main

import (
    "errors"
    "fmt"
    "log"
    "github.com/NERVsystems/cotlib"
)

func main() {
    // Create an event
    event, _ := cotlib.NewEvent("UNIT-123", "a-f-G", 37.422, -122.084, 0.0)
    
    // Set how value using descriptor (recommended)
    err := cotlib.SetEventHowFromDescriptor(event, "gps")
    if err != nil {
        log.Fatal(err)
    }
    // This sets event.How to "h-g-i-g-o"
    
    // Or set directly if you know the code
    event.How = "h-e" // manually entered
    
    // Validate how value
    if err := cotlib.ValidateHow(event.How); err != nil {
        if errors.Is(err, cotlib.ErrInvalidHow) {
            log.Fatal(err)
        }
    }
    
    // Get human-readable description
    desc, _ := cotlib.GetHowDescriptor("h-g-i-g-o")
    fmt.Printf("How: %s\n", desc) // Output: How: gps
}
```

#### Relation Values

Relation values specify the relationship type in link elements:

```go
// Add a validated link with parent-point relation
err := event.AddValidatedLink("HQ-1", "a-f-G-U-C", "p-p")
if err != nil {
    if errors.Is(err, cotlib.ErrInvalidRelation) {
        log.Fatal(err)
    }
}

// Or add manually (validation happens during event.Validate())
event.AddLink(&cotlib.Link{
    Uid:      "CHILD-1",
    Type:     "a-f-G",
    Relation: "p-c", // parent-child
})

// Validate relation value
if err := cotlib.ValidateRelation("p-c"); err != nil {
    if errors.Is(err, cotlib.ErrInvalidRelation) {
        log.Fatal(err)
    }
}

// Get relation description
desc, _ := cotlib.GetRelationDescription("p-p")
fmt.Printf("Relation: %s\n", desc) // Output: Relation: parent-point
```

#### Available Values

**How values include:**
- `h-e` (manual entry)
- `h-g-i-g-o` (GPS)
- `m-g` (GPS - MITRE)
- And many others from both MITRE and TAK specifications

**Relation values include:**
- `c` (connected)
- `p-p` (parent-point)
- `p-c` (parent-child)  
- `p` (parent - MITRE)
- And many others from both MITRE and TAK specifications

#### Validation

Event validation automatically checks how and relation values:

```go
event.How = "invalid-how"
err := event.Validate() // Will fail

event.AddLink(&cotlib.Link{
    Uid:      "test",
    Type:     "a-f-G", 
    Relation: "invalid-relation",
})
err = event.Validate() // Will fail
```

### Custom Types

You can register custom type codes that extend the standard prefixes:

```go
// Register a custom type
cotlib.RegisterCoTType("a-f-G-E-V-custom")

// Validate the custom type
if err := cotlib.ValidateType("a-f-G-E-V-custom"); err != nil {
    log.Fatal(err)
}

ctx := cotlib.WithLogger(context.Background(), logger)

// Register types from a file
if err := cotlib.RegisterCoTTypesFromFile(ctx, "my-types.xml"); err != nil {
    log.Fatal(err)
}

// Register types from a string
xmlContent := `
    
    
`
if err := cotlib.RegisterCoTTypesFromXMLContent(ctx, xmlContent); err != nil {
    log.Fatal(err)
}
```

### Generating Type Metadata (`cotgen`)

The `cmd/cotgen` utility expands the CoT XML definitions and writes the
`cottypes/generated_types.go` file used by the library. Ensure the
`cot-types` directory (or `cottypes` as a fallback) is present, then run:

```bash
go run ./cmd/cotgen
# or simply
go generate ./cottypes
```

Add your custom type entries to `cottypes/CoTtypes.xml` (or `cot-types/CoTtypes.xml`) before running the
generator to embed them into the resulting Go code.

The test suite ensures `generated_types.go` is up to date. If it fails,
regenerate the file with `go generate ./cottypes` and commit the result.

## TAK Types and Extensions

The library supports both canonical MITRE CoT types and TAK-specific extensions. TAK types are maintained separately to ensure clear namespace separation and avoid conflicts with official MITRE specifications.

### Adding New CoT Types

**For MITRE/canonical types:** Add entries to `cottypes/CoTtypes.xml`
**For TAK-specific types:** Add entries to `cottypes/TAKtypes.xml`

The generator automatically discovers and processes all `*.xml` files in the `cot-types/` directory (falling back to `cottypes/` if needed).

### TAK Namespace

All TAK-specific types use the `TAK/` namespace prefix in their `full` attribute to distinguish them from MITRE types:

```xml

```

### Working with TAK Types

```go
// Check if a type is TAK-specific
typ, err := cottypes.GetCatalog().GetType("b-t-f")
if err != nil {
    log.Fatal(err)
}

if cottypes.IsTAK(typ) {
    fmt.Printf("%s is a TAK type: %s\n", typ.Name, typ.FullName)
    // Output: b-t-f is a TAK type: TAK/Bits/File
}

// Search for TAK types specifically
takTypes := cottypes.GetCatalog().FindByFullName("TAK/")
fmt.Printf("Found %d TAK types\n", len(takTypes))

// Validate TAK types
if err := cotlib.ValidateType("b-t-f"); err != nil {
    log.Fatal(err) // TAK types are fully validated
}
```

### Generator Workflow

1. The generator scans `cot-types/*.xml` (or `cottypes/*.xml`) for type definitions
2. Parses each XML file into the standard `` structure  
3. Validates TAK namespace integrity (no `a-` prefixes with `TAK/` full names)
4. Expands MITRE wildc

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [NERVsystems](https://github.com/NERVsystems)
- **Source:** [NERVsystems/cotlib](https://github.com/NERVsystems/cotlib)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-nervsystems-cotlib
- Seller: https://agentstack.voostack.com/s/nervsystems
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
