- Swift 100%
AUD-04-12: domainHints added (12-key parity with the Python canonical); collation locked to raw Unicode code-point order via public codePointLess (Swift's default String < is Unicode-canonical — divergent); golden parity fixture byte-locked vs the Python + TS siblings. AUD-04-13: canonicalEmitNames reduced to ["appendMeta"] — makeResultWithMeta is a per-blade wrapper, not a library symbol; bare-name trust let drifted wrappers lint structured. Wrappers now satisfy S-AUD-001 only via resolved bodies (free functions + new typeMethodsIndex method fallback); unresolvable switch-dispatch handlers yield indeterminate, never a false over-declared. AUD-04-08 class: new Transport.swift — token-absent HTTP refuses to serve, wildcard binds refused unconditionally, strict-bool non-loopback opt-in, CryptoKit constant-time validateBearer (websocket-upgrade enforcement doc). 79 tests / 6 suites green (per-suite run confirmed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| Sources | ||
| Tests/MCPHelpersTests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| Package.swift | ||
| README.md | ||
stallari-mcp-helpers (Swift)
Canonical helpers for Swift MCP servers targeting Stallari's contract surface — a tight Swift Package that lets your tools emit the _meta audit envelope the Stallari assembler expects, without you rolling your own.
If you're porting a Swift MCP server to Stallari and want first-party-tier conformance, this is the on-ramp.
About Stallari
Stallari is an agentic personal-knowledge-management platform. It runs MCP servers as its tool surface — the way it talks to email providers, calendars, smart-home hubs, source control, cloud infra, etc. Your MCP server dispatches through Stallari's assembler, which audits each tool call against a wire-shape contract before lifting the result into the LLM's context. This package provides the canonical helpers that make a Swift MCP server emit that contract cleanly.
Sister packages
| Language | Package | Source |
|---|---|---|
| Python | stallari-mcp-helpers on PyPI |
stallari-mcp-helpers |
| TypeScript | stallari-mcp-helpers on npm |
stallari-mcp-helpers-ts |
| Swift | MCPHelpers via Swift Package Manager |
this repo |
All three packages stay in lockstep on the wire shape — meta_envelope(...) in Python, formatMetaLine(...) in TypeScript, and formatMetaLine(...) in Swift all emit byte-equivalent _meta envelopes for the same input.
What this is
One small module:
MCPHelpers— renders the canonical_meta: {...}JSON-tail block the Stallari assembler lifts into itsContextPacket.provenanceaudit trail. Locked encoding: tight JSON separators, code-point-sortedfiltered_byanddomain_hintskeys (raw Unicode scalar order, no normalisation —codePointLess, NOT Swift's defaultString <), required/optional field discipline matching the DD-338 wire contract (12 keys as of v0.4.0).- Plus the canonical HTTP transport policy (
Transport.swift, v0.4.0 / AUD-04-08) —resolveHTTPTransport(refuse-to-serve when the token is absent, wildcard binds never permitted, non-loopback requires an exact-"true"opt-in) andvalidateBearer(constant-time bearer comparison; enforce on WebSocket upgrades too).
This package does not ship MCP-server scaffolding, tool registration, an HTTP server, inference, or domain-attribution primitives. It's deliberately small.
Audience
Direct consumers (future, post Stallari W5 Swift wave):
- First-party Stallari Swift MCP servers —
apple-mail-blade-mcp,apple-notes-blade-mcp, and the Swift surface ofstallari-vaultserved asstallari-blade-mcpvia the daemon. These don't yet ship the_metaenvelope; this package lands ahead of W5 so the wave can adopt natively.
Broader audience:
- Any Swift MCP author who wants their server to dispatch through Stallari at first-party-tier conformance. You don't have to be on the Stallari team. Add the SPM dependency, use the helpers, declare your tool capabilities honestly in your pack manifest, and the assembler will treat your tool as a first-class participant.
If you're integrating a third-party MCP that you don't control, the Stallari adapter-transform layer (deterministic YAML, in-process) handles you separately — you don't need this library.
Install (Swift Package Manager)
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/Groupthink-dev/stallari-mcp-helpers-swift", from: "0.1.0"),
],
targets: [
.target(
name: "MyBlade",
dependencies: [
.product(name: "MCPHelpers", package: "stallari-mcp-helpers-swift"),
]
),
]
Requires Swift 6.0+, macOS 14+ or iOS 17+.
Quick start — emitting a _meta envelope
import MCPHelpers
func mySearchTool(query: String, scope: String = "personal") async throws -> String {
let records = try await upstreamAPI.search(query, filter: "scope=\(scope)")
let body = formatRecords(records)
let meta = MetaEnvelope(
matchedTotal: records.totalMatched,
returned: records.items.count,
latencyMs: records.latencyMs,
filteredBy: ["scope=\(scope)", "query=\(query)"],
redactions: [], // required, empty allowed
nextCursor: nil // required, nil allowed
)
return appendMeta(body, try formatMetaLine(meta))
}
Output (assembler-side regex contract: \n\n_meta: (\{.*\})$):
<your body>
_meta: {"matched_total":42,"returned":10,"latency_ms":234,"filtered_by":["query=foo","scope=personal"],"redactions":[],"next_cursor":null}
JSON keys are emitted with snake-case names (matched_total, latency_ms, filtered_by, next_cursor) for byte-parity with the Python and TypeScript sister packages — Swift idiomatic camelCase on the API surface, canonical snake-case on the wire.
Your manifest
Per tool in your pack catalog entry:
{
"name": "my_search_tool",
"granularity": {
"scope_filtering": "server-side",
"deterministic_ordering": "stable",
"audit_surface": "structured",
"domain_scope": "single"
}
}
Four honest declarations. If you implement scope_filtering: server-side you must actually filter at the upstream API. If you declare deterministic_ordering: stable your output must be reproducible. If you declare audit_surface: structured your tool must emit formatMetaLine(...) on every call.
Stallari's conformance harness verifies these claims against your actual tool behaviour at pack-acceptance time. Honest degraded declarations always pass; lying about a capability fails. Start with the most conservative declarations and bump each axis as you implement the corresponding behaviour.
What you don't have to think about
- Exact JSON encoding of
_metaenvelopes (separator choice, sort order) - Field-presence rules (required vs optional)
- Whether to round
latencyMs - The assembler-side
ContextPacketshape that consumes your envelopes - Future contract evolution — the library version-pins the wire shape; the Python and TypeScript sisters stay in lockstep
API reference
public struct MetaEnvelope: Sendable, Equatable, Codable {
public let matchedTotal: Int? // optional since D.1, omit-when-nil
public let returned: Int? // optional since D.1, omit-when-nil
public let latencyMs: Int
public let filteredBy: [String] // code-point sorted in output
public let redactions: [String] // required, defaults to []
public let nextCursor: String? // required, nil emits JSON null
public let rowsAffected: Int? // write-tier, omit-when-nil
public let targetId: String? // write-tier, omit-when-nil
public let writeDurability: String? // "edge" | "central" | "replicated"
public let responseTimestamp: String? // write-tier, omit-when-nil
public let errorNotes: [String]? // omitted when nil or empty
public let domainHints: [String: String]? // v0.4.0; omitted when nil or
// empty; keys code-point sorted
public init(
matchedTotal: Int? = nil,
returned: Int? = nil,
latencyMs: Int,
filteredBy: [String] = [],
redactions: [String] = [],
nextCursor: String? = nil,
rowsAffected: Int? = nil,
targetId: String? = nil,
writeDurability: String? = nil,
responseTimestamp: String? = nil,
errorNotes: [String]? = nil,
domainHints: [String: String]? = nil
)
}
public func formatMetaLine(_ meta: MetaEnvelope) throws -> String
public func appendMeta(_ payload: String, _ metaLine: String) -> String
// Locked cross-language collation (raw Unicode code-point order, no
// normalisation — Swift's default `String <` diverges):
public func codePointLess(_ lhs: String, _ rhs: String) -> Bool
// HTTP transport policy (v0.4.0, AUD-04-08 / AUD-04-31):
public enum TransportPolicyError: Error, Equatable, CustomStringConvertible
public struct HTTPTransportConfig: Sendable, Equatable {
public let host: String
public let port: Int
public let token: String
}
public func strictEnvBool(_ value: String?) -> Bool // true ONLY for "true"
public func resolveHTTPTransport(
envPrefix: String,
defaultPort: Int,
environment: [String: String]? = nil,
tokenVariable: String? = nil
) throws -> HTTPTransportConfig
public func validateBearer(authorizationHeader: String?, expectedToken: String) -> Bool
Locked 12-key _meta emission order (v0.4.0): matched_total, returned, filtered_by, latency_ms, redactions, next_cursor, rows_affected, target_id, write_durability, response_timestamp, error_notes, domain_hints.
Versioning
SemVer. 0.x.y series while the public API stabilises; 1.0.0 once consumer blade-mcps have shipped against 0.x for >30 days with no API churn.
Breaking changes after 1.0.0 get a one-minor-version deprecation window (v1.y annotates @available(deprecated), v2.0 removes).
License
MIT. See LICENSE.
Contributing
This is internal Stallari plumbing during the 0.x series — issues + PRs accepted but the API surface is still settling. Submit issues at the GitHub tracker.