Hands-On: Build a Claude Code Plugin, an OpenAI App, and a Gemini Agent
Three walkthroughs, one weekend: package skills, commands and MCP servers into a Claude Code plugin; ship a ChatGPT app (and Codex plugin) on top of an MCP server; build and deploy a Gemini agent with ADK. Each ends with a shippable artifact.
After this lesson you can
- βScaffold, test and distribute a Claude Code plugin from a git repo
- βTurn an MCP server into a ChatGPT app with a rendered UI, and reuse it as a Codex plugin
- βBuild a Gemini ADK agent with tools and sessions, run it locally, and deploy it to Vertex AI Agent Engine
Three platforms, one mental model
Each ecosystem has a way to extend its agent surface with your own capabilities, and they rhyme. A Claude Code plugin is a git-distributable folder bundling skills, slash commands, sub-agents, hooks and MCP servers. An OpenAI app is an MCP server plus UI metadata that ChatGPT renders inline (and the same server plugs into Codex). A Gemini agent built with ADK is a Python or Java object graph of agents and tools with a local dev UI and a managed deployment target. In all three, MCP is the tool boundary and a markdown instructions file is the knowledge boundary.
| Claude Code plugin | OpenAI app / Codex plugin | Gemini ADK agent | |
|---|---|---|---|
| What you ship | A folder in a git repo | An MCP server (HTTP) + widget resources | A Python/Java package (or container) |
| Manifest | .claude-plugin/plugin.json | Server metadata + tool _meta (openai/*) | agent.py exposing root_agent; deployment config |
| Bundles | skills/, commands/, agents/, hooks/, .mcp.json | Tools, resources (HTML widgets), auth | LlmAgent tree, tools, callbacks, sessions |
| Install / run | /plugin marketplace add Β· /plugin install | Developer mode β add connector by URL | adk web (local) Β· adk deploy agent_engine |
| Distribution | Marketplace JSON in any git repo | App submission / directory; org-internal by URL | Agent Engine endpoint; A2A card |
Part 1 β A Claude Code plugin
A plugin is the unit of distribution for everything Claude Code can be taught. Skills teach procedure; commands give users a /slash entry point; agents define sub-agents with their own prompt and tool allowlist; hooks run shell commands on lifecycle events (pre/post tool use, session start); .mcp.json attaches tool servers. Only the manifest is required β start with one skill and grow.
Testing loop: run claude --plugin-dir ./my-plugin to load it without installing, invoke the command, iterate. Distribution: add a marketplace.json to any git repo (your plugin repo can be its own marketplace), then teammates run /plugin marketplace add your-org/your-repo and /plugin install release-tools@your-org. Version the plugin in plugin.json and Claude Code handles updates.
release-tools/
βββ .claude-plugin/
β βββ plugin.json
βββ skills/
β βββ release-notes/
β βββ SKILL.md # from lesson 2 β unchanged
βββ commands/
β βββ ship.md # becomes /release-tools:ship
βββ agents/
β βββ changelog-reviewer.md # a sub-agent
βββ hooks/
β βββ hooks.json
βββ .mcp.json
# .claude-plugin/plugin.json
{
"name": "release-tools",
"version": "1.0.0",
"description": "Release notes, ship checklist and changelog review.",
"author": { "name": "Your Team" }
}
# commands/ship.md
---
description: Run the ship checklist for the current branch
allowed-tools: Bash(git *), Read
---
Run the release checklist: confirm CI is green for $ARGUMENTS, load the
release-notes skill, draft notes from `git log main..HEAD`, and stop for
approval before tagging.
# agents/changelog-reviewer.md
---
name: changelog-reviewer
description: Reviews draft release notes for missing breaking changes. Use after drafting notes.
tools: Read, Grep
model: haiku
---
You review release notes against the diff. Flag any public API change not
listed under Breaking. Return a checklist, nothing else.
# hooks/hooks.json β block tagging without notes
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{ "type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/guard-tag.sh" }]
}]
}
}
# .mcp.json β tools the plugin brings with it
{ "mcpServers": { "orders": { "type": "http", "url": "https://mcp.example.com/orders" } } }
# Try it: claude --plugin-dir ./release-tools
# Distribute: add .claude-plugin/marketplace.json listing the plugin, push, then
# /plugin marketplace add your-org/release-tools
# /plugin install release-tools@your-orgPart 2 β An OpenAI app (and Codex plugin)
The original ChatGPT plugins (OpenAPI manifests, 2023) were retired. Todayβs path is the Apps SDK: you build an MCP server, and ChatGPT becomes the client. Tools work as normal MCP tools; the addition is metadata under the openai/* namespace in each toolβs _meta that points at an HTML resource ChatGPT renders inline as a widget β a table of orders, a map, a form. The widget talks back to your server through the same MCP connection.
Development loop: run the server locally, expose it over HTTPS (ngrok or a tunnel), enable developer mode in ChatGPT, add the server as a connector by URL, and chat. Authentication for real users is OAuth 2.1 with dynamic client registration β the MCP standard β so the same server is installable in Codex, Claude and Gemini CLI with no code change. A Codex plugin wraps that server with skills and prompts for the coding harness, exactly as the Claude plugin does.
# The same server is a plain MCP server for Claude β the openai/* metadata # is simply ignored. Attach it in Claude Code: # # claude mcp add --transport http orders https://your-tunnel.example/mcp # # or in a plugin's .mcp.json (Part 1), or via the Messages API mcp_servers # connector (lesson 5). The widget HTML is not rendered; the model uses the # structured tool result, which is why the tool must return real data and # not only a UI.
Part 3 β A Gemini agent with ADK
ADK is Googleβs code-first agent framework: an LlmAgent has a model, instructions, tools and optional sub-agents; workflow agents (SequentialAgent, ParallelAgent, LoopAgent) compose them deterministically; a Runner executes an agent against a SessionService that persists state across turns. The killer feature for learning is adk web β a local UI that shows every event, tool call and state change in the loop.
Project layout is a package with an agent.py exporting root_agent. Run adk web from the parent directory and pick your agent. Deployment: adk deploy agent_engine pushes it to Vertex AI Agent Engine, which gives you a managed endpoint, sessions and memory. Expose it to other agents with the A2A card (to_a2a) when another teamβs agent needs to call yours.
# pip install claude-agent-sdk
import asyncio
from claude_agent_sdk import (ClaudeSDKClient, ClaudeAgentOptions,
tool, create_sdk_mcp_server)
@tool("order_status", "Look up the shipping status of an order by ID (ORD-123456).",
{"order_id": str})
async def order_status(args):
status = lookup(args["order_id"]) # your data layer
return {"content": [{"type": "text", "text": f"{args['order_id']}: {status}"}]}
tools_server = create_sdk_mcp_server("support", tools=[order_status])
options = ClaudeAgentOptions(
system_prompt="You are a support agent. Use order_status for any order query.",
mcp_servers={"support": tools_server},
allowed_tools=["mcp__support__order_status"],
model="claude-sonnet-4-5",
)
async def main():
async with ClaudeSDKClient(options=options) as client: # one session, many turns
await client.query("Where is ORD-482913?")
async for m in client.receive_response(): print(m)
await client.query("And has that order been paid?") # context carries over
async for m in client.receive_response(): print(m)
asyncio.run(main())
# Hosting: run this anywhere Python runs, or use Anthropic's Managed Agents
# for a server-hosted sandboxed runtime.What to build this weekend
Pick one internal procedure your team repeats β release notes, on-call triage, a data-quality check. Write it as a SKILL.md. Wrap the systems it touches in one MCP server. Then package it three ways: a Claude Code plugin, a ChatGPT app / Codex plugin, and an ADK agent with a Gemini CLI extension. You will end with one skill, one server and three thin wrappers β and a very concrete answer to every "have you built agents?" interview question.
- β’Day 1: SKILL.md + MCP server with two tools; test in Claude Code with --plugin-dir and in Gemini CLI via settings.json
- β’Day 2 morning: plugin manifest + marketplace.json; ChatGPT developer-mode connector with one widget
- β’Day 2 afternoon: ADK agent using the same server via MCPToolset; adk web, then adk deploy agent_engine
- β’Write down what differed between the three β that list is your interview story
Interview questions this lesson prepares you for
- What goes into a Claude Code plugin, and how does it get to your teammatesβ machines?
- How does a ChatGPT app differ from an ordinary MCP server? What is the role of _meta?
- Describe the ADK object model: agents, tools, runner, session service. Where does state live?
- You built a tool server once. How do you make it usable from all three ecosystems?