Install
$ agentstack add mcp-nervsystems-cotlib ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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
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
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
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
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__grouparchiveattachmentListenvironmentfileshareprecisionlocationtakvtrackmissionstatusshapestrokecolorstrokeweightfillcolorlabelsonuidbullseyerouteInfocolorhierarchylinkusericonemergencyheightheight_unitremarks
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:
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.
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:
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:
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, orusegments to catalog
entries containing .
// 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:
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:
// 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:
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:
// 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:
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:
Working with TAK Types
// 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
- The generator scans
cot-types/*.xml(orcottypes/*.xml) for type definitions - Parses each XML file into the standard `` structure
- Validates TAK namespace integrity (no
a-prefixes withTAK/full names) - 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
- Source: NERVsystems/cotlib
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.