Sharing AI practices across a teamPart 1

Build a team CLI for shared AI skills and tools

A hands-on Go example that installs team-owned skills and rules, configures coding agents, and runs a useful local MCP server.

The most useful AI workflow on a team often starts with one person. Someone writes a good review skill, works out a repository rule that prevents a recurring mistake, or connects an agent to a small internal tool. It helps. Then it stays on that person’s laptop.

This series builds one practical way to share those wins: a small Go CLI owned by the team. A teammate installs the binary and uses it to set up the coding agent in the repository where they are working. The same binary can also run a local MCP server containing tools the team maintains.

The first version supports a deliberately useful command surface:

$ team-ai setup --target claude
$ team-ai skills install --target codex --name review-change
$ team-ai rules install --target cursor --name safe-changes
$ team-ai mcp install --target claude
$ team-ai mcp serve

There is no project generator in that list. The CLI’s job is to deliver things the team has already found useful.

Start from the teammate’s workflow

Imagine joining an existing service and running:

$ team-ai setup --target claude
team setup: 5 installed, 0 already current in .

That command installs three kinds of team-owned material:

  • task-specific skills under .claude/skills/;
  • shared working rules in a managed section of CLAUDE.md;
  • .mcp.json, configured to launch the local server with team-ai mcp serve.

The Codex and Cursor adapters write the paths those tools expect. The source remains the same set of files in the team repository.

Running the command again is intentionally uneventful:

$ team-ai setup --target claude
team setup: 0 installed, 5 already current in .

That is the basic developer experience for the example. Clone or install the team CLI, run one command in a working repository, and receive the current shared setup.

Keep the team-owned material next to the code

The repository is a normal Go project with a visible team/ directory:

team/
  embed.go
  knowledge/
    repository-practices.md
  rules/
    safe-changes.md
    evidence-before-confidence.md
  skills/
    review-change/
      SKILL.md
    incident-handoff/
      SKILL.md
      template.md
cmd/team-ai/
internal/
  catalog/
  cli/
  mcp/
  render/
  syncer/

Skills follow the portable Agent Skills specification, so a skill is a directory containing SKILL.md and any references, scripts, or templates it needs. Rules are short Markdown files with frontmatter describing their name, priority, and applicable targets. Knowledge files are ordinary Markdown consumed by the local MCP tools.

The files are embedded in the released binary:

package team

import "embed"

//go:embed rules skills knowledge
var Files embed.FS

This makes the distribution model pleasantly small. A team reviews a change to a skill, rule, or tool, releases the CLI, and teammates install that version. The binary and the shared material cannot quietly drift apart.

Install all of it or only what is useful

setup is the convenient path, but teammates can be selective. Someone who wants the review skill without changing repository instructions can run:

$ team-ai skills install \
    --target codex \
    --name review-change
skills: 1 installed, 0 already current in .

The CLI copies the complete skill directory, including its references and templates, into .agents/skills/review-change/. Repeating --name installs several skills. Omitting it installs all team skills.

Rules have the same shape:

$ team-ai rules install \
    --target cursor \
    --name safe-changes
rules: 1 installed, 0 already current in .

For Cursor, the adapter writes .cursor/rules/safe-changes.mdc. For Codex and Claude Code, it renders the selected rules into a marked section of AGENTS.md or CLAUDE.md. Codex discovers repository instructions through AGENTS.md according to its official configuration documentation.

The command hierarchy maps to the task a teammate has in mind: set up everything, install skills, install rules, or configure the MCP server. The internal catalog and adapters support that workflow; they are not the user experience themselves.

Run a local MCP server the team owns

The example MCP server is part of the Go binary. It exposes two small tools:

  • search_team_knowledge searches the Markdown files embedded from team/knowledge/;
  • get_team_skill returns a team skill by name.

They are intentionally simple, but they are real MCP tools. A production team could use the same boundary for an internal service catalog, architecture records, issue requirements, deployment metadata, or another source that has proved useful in daily work.

The stdio server is a JSON-RPC loop:

func (s Server) Serve(ctx context.Context, in io.Reader, out io.Writer) error {
    scanner := bufio.NewScanner(in)
    encoder := json.NewEncoder(out)

    for scanner.Scan() {
        var req request
        if err := json.Unmarshal(scanner.Bytes(), &req); err != nil {
            // Return a JSON-RPC parse error.
            continue
        }

        result, rpcErr := s.handle(req)
        if len(req.ID) > 0 {
            if err := encoder.Encode(response{
                JSONRPC: "2.0",
                ID:      req.ID,
                Result:  result,
                Error:   rpcErr,
            }); err != nil {
                return err
            }
        }
    }

    return scanner.Err()
}

initialize, ping, tools/list, and tools/call are enough for the first working slice. The server writes protocol messages to stdout and nothing else. Logs and diagnostics belong on stderr because one friendly banner on stdout is enough to break the MCP connection.

You can exercise it without an agent:

$ printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | team-ai mcp serve
{"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}

Let the CLI configure the MCP client

Nobody should need to remember the MCP configuration by hand. This command finds the absolute path of the running binary and configures the selected client:

$ team-ai mcp install --target claude
MCP server: 1 installed, 0 already current in .

The generated .mcp.json is ordinary configuration:

{
  "mcpServers": {
    "team-ai": {
      "command": "/usr/local/bin/team-ai",
      "args": ["mcp", "serve"]
    }
  }
}

Claude Code uses .mcp.json for shared project servers, while Cursor uses .cursor/mcp.json. Codex stores MCP registrations in its user configuration, so that adapter calls the native command directly:

$ team-ai mcp install --target codex
Added global MCP server 'team-ai'.

Under the hood, that is codex mcp add team-ai -- /usr/local/bin/team-ai mcp serve. The installer first inspects an existing registration and treats an identical command as current. If the same name points somewhere else, it stops instead of replacing configuration the teammate already owns.

The configuration launches a local process. It does not send team knowledge to a new service or introduce another daemon to operate. Access control remains explicit: the MCP process has the same local permissions as the coding agent that starts it.

Preserve work already owned by the repository

Installation is useful only if teammates trust it. The example distinguishes between two kinds of output.

For human-authored instruction files, the CLI owns only a marked section:

# Repository-specific notes

Run the service's integration tests before opening a pull request.

<!-- team-ai:start -->

# Shared AI working agreement

...

<!-- team-ai:end -->

Everything outside the markers stays untouched. Skill files and project MCP configuration for Claude and Cursor are whole-file outputs, so the CLI records their SHA-256 digests in .team-ai.lock.json. If somebody edits managed output locally, a later install reports the conflict instead of replacing the edit. Codex’s user-scoped MCP registration is checked separately through its CLI.

The installer also preflights every planned output before writing any of them. A conflict in .mcp.json cannot leave half the skills installed and the rules missing.

Make team ownership visible

The repository becomes useful when contribution is ordinary:

  1. A teammate notices a repeated success or frustration while working with an agent.
  2. They update a skill, rule, knowledge file, or MCP tool in this repository.
  3. The team reviews the change using the same engineering judgment it uses elsewhere.
  4. A new CLI version makes the improvement available to everyone.
  5. More teammates use it and bring back better examples.

Not every discovery needs to become a permanent rule. Some belong in a focused skill. Some become a local MCP tool. Some are only relevant to one repository and stay there. The CLI provides a shared place and a delivery path; the team supplies the judgment.

A team-owned CLI delivers shared AI tools

Open in Studio
The repository carries reviewed skills, rules, knowledge, and MCP tools. Teammates install them into the coding agents they already use.

Where the series goes next

This first part gives the team something it can actually run. Part two will cover releasing and updating the CLI, selective installs, managed-file ownership, and safe changes across several agent formats. Part three will focus on the contribution loop: turning a teammate’s successful workflow into a skill, rule, knowledge source, or MCP tool that other people can use and improve.