Tattoo Feed
The Tattoo Pal server lets you discover, curate, and save tattoo inspiration from Instagram artists within an AI chat interface. All interactions are read-only — it never posts, comments, or messages on Instagram.
Artist Management
Add, list, or remove Instagram artists by handle to build your tracked list.
Feed & Discovery
Browse a merged feed — retrieve recent posts (metadata + permalinks) from all tracked artists, newest first.
Get next inspiration — surface one previously unseen post at a time, rendered as an inline image widget in ChatGPT.
Reset seen posts — clear the seen-set to restart discovery from the beginning.
Curation & Bookmarking
Save posts — bookmark any post by ID with optional notes.
List saved items — view your personal inspiration collection in saved order.
Remove saved items — delete posts from your collection by ID.
Taste Tracking
Record preferences — persist natural-language style observations (e.g. "prefers fine-line botanical work"), requiring user confirmation before saving.
Retrieve preference summary — reload your full taste profile in a new session.
Provides tools to browse and curate posts from Instagram tattoo artists via Instagram's Business Discovery API, allowing LLM clients to list artists, fetch feeds, discover inspirations, bookmark posts, and record preferences.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Tattoo FeedShow me my next inspiration"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Tattoo Pal
A calm, bounded way to spend time with the tattoo artists you admire — inside your AI chat, without Instagram's feed.
Tattoo Pal brings an artist's recent work to you one piece at a time, in a conversation, so you can sit with it, react, keep what resonates, and over time see how your own taste shows up across real work. It is the opposite of an infinite algorithmic scroll: you look at what you asked for and leave when you're done, not when a feed decides to release you.
Under the hood it is a Model Context Protocol (MCP) server. You point it at the artists you follow (by Instagram handle); from your chat client you can pull a merged feed, discover one post at a time, bookmark favourites, and record notes about your taste that a future session can reload. Recent work is fetched through Instagram's Business Discovery API.
The image renders inline only in ChatGPT. next_inspiration returns a
ChatGPT Apps SDK widget that draws the preview image directly in the
conversation. This matters because no other tested client shows the image inline
— ChatGPT does not render raw MCP image blocks, and Claude receives the image
but does not display it. Since actually seeing the work is the whole point, the
product targets the ChatGPT connector over HTTP. A stdio transport exists for
local development, but it does not render the image.
Read-and-curate only: Tattoo Pal never posts, comments, or messages, and previews are downscaled copies — see Attribution.
How it works
You ──▶ ChatGPT ──(OAuth-gated HTTP)──▶ Tattoo Pal (MCP server) ──▶ Instagram
│ Business
renders the ◀────┘ next_inspiration Discovery
Apps SDK widget returns a widget +
(image inline) downscaled previewMCP tools expose the actions (track artists, pull a feed, discover one piece, save, record taste). See The MCP tools.
Two-layer codebase: all logic lives in a transport-agnostic
core; a thinserveradapter exposes it as MCP. See Repository layout.OAuth 2.1 resource server: in HTTP mode every request must carry a valid bearer token from your identity provider. An unauthenticated request gets
401with aWWW-Authenticate: Bearer resource_metadata=...header (RFC 9728); the ChatGPT connector follows this to complete login automatically.The widget: the ≤640px preview travels as a data URL in the tool result's
_meta, which the ChatGPT host forwards to the widget iframe without ever putting the base64 through the model's context window.
Related MCP server: Instagram MCP Server
Repository layout
Code — src/tattoo_feed/
A deliberate two-layer split so a future GUI can reuse the logic without a
rewrite. core knows nothing about MCP; server holds no business logic.
src/tattoo_feed/
config.py # lazy env config (IG_ACCESS_TOKEN, IG_USER_ID)
errors.py # typed error hierarchy (TattooFeedError + subclasses)
models.py # Pydantic v2 frozen value objects
imaging.py # preview downscale + EXIF strip (≤640px JPEG)
repositories/ # Repository ABC + JSON-file stores (atomic writes)
graph/client.py # Instagram Business Discovery client
services/ # Feed / Artist / Inspiration / Preference services
server/app.py # build_server() factory; MCP tools; stdio/HTTP entrypoint
server/auth.py # OAuth 2.1 JWT verifier (resource-server side)
server/widgets/ # Apps SDK widget HTML, served as a ui:// MCP resourcetests/ mirrors this with a fully hermetic suite — Instagram HTTP is mocked
with respx, JWKS with test-generated RSA keypairs, zero live network calls.
Non-code documentation
The repo keeps two kinds of written record alongside the code. Neither is needed to run the project; both are kept deliberately, as a window into how it was built.
scratchpads/— in-build engineering notes and design deep-dives. The working reasoning behind specific decisions and bug investigations, written as they happened: e.g.removing-the-global.md(why the module-level server global was removed),auth-wiring-seam.mdandhost-header-421.md(an auth refactor and the DNS-rebinding421it surfaced),rate-limiting.md,built-for-chatgpt.md. Think of these as the project's lab notebook.build_artifacts/— an archived record of the phased build. The project was built in stages (Phase 1–Phase 3), each driven by its own governing docs — an implementation plan, a technical-contract reference, process rules, and an acceptance checklist — plus the autonomous build-loop scripts and the per-phase build logs. It is purely historical: a snapshot of how each stage was specified and run, not live configuration.Root docs —
RETROACTIVE_PRD.mdreconstructs, at product altitude, the why / for whom / what "good" means (a teaching artifact written after the fact);CLAUDE.mdis the process governance for the build tooling.
Setup
Requirements: Python 3.12 and uv.
uv sync # create the venv and install pinned deps
cp .env.example .env # then edit .env with your real credentialsEnvironment variables
Always required — Instagram credentials:
Variable | Meaning |
| A long-lived Instagram Graph API access token. |
| The Instagram Business/Creator account id that owns the token (not your Facebook user id). |
| Optional. Where the JSON stores live (default |
Required for HTTP / ChatGPT mode — OAuth resource-server config:
Variable | Meaning |
| Issuer URL of your IdP — must exactly match the token's |
| JWKS endpoint used to verify JWT signatures. |
| Canonical public URL of this server — the RFC 8707 audience binding. |
| Comma-separated required scopes (blank for none). |
| ngrok auth token and your reserved domain for stable TLS ingress. |
.env is gitignored and must never be committed — only .env.example (with
placeholders) is in the repo.
Getting Instagram credentials is a one-time manual step on Meta's side: create a Meta app, link an Instagram Business/Creator account to a Facebook Page, and mint a long-lived token with Business Discovery permission. The
IG_USER_IDmust be the Instagram account id (viaGET /me/accounts?fields=instagram_business_account), not your Facebook user id.
Running
ChatGPT over HTTP — the product
./run-server.sh builds the image and starts the server + ngrok tunnel together
(a thin wrapper over docker compose up --build):
cp .env.example .env # fill in all values, including MCP_AUTH_* and NGROK_*
./run-server.shThen:
Open the ngrok inspector at
http://localhost:4040to confirm the public URL.In ChatGPT, add a custom connector:
URL:
https://<your-ngrok-domain>/mcpAuthentication: OAuth
ChatGPT walks through OAuth discovery and a browser login against your IdP, then lists the tools.
Identity provider. The server is IdP-agnostic — it only needs an issuer
supporting OAuth 2.1 + PKCE, metadata discovery (RFC 8414 / OIDC), and the RFC
8707 resource indicator. Auth0
has a documented walkthrough for exactly this setup (Stytch, WorkOS, Descope are
alternatives). Configure your IdP to issue tokens whose audience is the server's
public URL (MCP_AUTH_AUDIENCE), and register a matching API/resource for it.
Stable domain. Reserve a domain at
https://dashboard.ngrok.com/domains and set NGROK_DOMAIN in .env, so the
public URL — and the connector configuration — survive restarts.
Local development (stdio)
A credential-free local entrypoint, useful for exercising the tools without a tunnel or IdP, and the transport the test suite boots over. It does not render the inspiration image — use ChatGPT for the visual experience.
uv run python -m tattoo_feed.server.appWire it into a local MCP client with command: "uv",
args: ["run", "python", "-m", "tattoo_feed.server.app"], the project as cwd,
and IG_ACCESS_TOKEN / IG_USER_ID in env.
The MCP tools
Tool | What it does |
| List tracked artists. |
| Validate the handle is a reachable professional account, then track it. |
| Stop tracking a handle. |
| Merged, newest-first feed — metadata + permalinks only (no images). |
| One not-yet-seen post, marked seen. Returns the Apps SDK widget; image renders inline only in ChatGPT. |
| Bookmark a post into the saved collection. |
| The saved collection, in save order. |
| Remove a saved item. |
| Clear the seen-set so inspiration starts fresh. |
| Persist a taste note (propose-then-confirm). |
| All recorded preferences, to reload taste in a fresh session. |
Design notes
Two-layer split (core / server). MCP concepts never leak into
core; business logic never leaks intoserver. A future GUI is a bolt-on, not a rewrite.JSON-file persistence behind a
Repositoryinterface. Simple, inspectable, swappable. Writes are atomic (temp file +os.replace) so a crash mid-write can't corrupt a store.Lazy credentials. The server boots and lists its tools with no network and no real credentials; tokens are read only when a tool calls Instagram or the auth middleware validates a bearer token.
Constructor-injected auth via a factory.
build_server(auth_cfg)is the single place a server instance is created, with auth supplied through the SDK's publicauth=/token_verifier=parameters — no private-attribute writes. PassingNonebuilds the unauthenticated stdio server.Widget image as a data URL in
_meta. Onlynext_inspirationreturns a rendered image — the one-at-a-time moment that earns the context.get_feedstays metadata-only to keep the context window light.Typed errors, frozen models, strict typing. Every external failure maps to a
TattooFeedError; boundary data is validated once into immutable Pydantic v2 values;mypy --strict,ruff, and a 90% coverage floor are enforced.
Limitations (by design)
Inline image rendering is ChatGPT-only — the Apps SDK widget is the one channel that shows the image; no other tested client displays it.
No video, carousels show the first image only. Still imagery, filtered at the Graph-client layer.
Single account. Wired to one Instagram account (
IG_USER_ID); OAuth gates who may call, not which account is queried.Manual token refresh. No auto-refresh; an expired token fails with a clear
TokenExpiredError.Resource-server only. The server validates tokens but does not issue them — it relies on an external IdP that must be configured first.
Widget render is human-verified. The gate confirms the widget is registered and the
_metais present; whether the image actually paints in ChatGPT is an eyeball check.
Attribution & copyright
Posts belong to the artists who made them. This tool is for personal discovery and curation, not redistribution:
Previews are downscaled copies (≤640px, EXIF stripped), never full-res.
Every image and saved item carries the artist's handle and the post's permalink, so attribution travels with the content.
Respect each artist's rights: don't repost or reuse their work without permission.
Development
The full gate (all must exit 0):
uv run ruff format --check .
uv run ruff check .
uv run mypy --strict src
uv run pytest -q --cov=src/tattoo_feed --cov-report=term-missing --cov-fail-under=90License
MIT — see LICENSE.
Available Tools
11 toolsadd_artistA
Track a new artist by Instagram handle.
The handle must resolve to a reachable professional (Business/Creator) account; otherwise a clear error is returned.
| Name | Required | Description | Default |
|---|---|---|---|
| handle | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| handle | Yes | |
| ig_user_id | No | |
| added_at | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full behavioral burden. It discloses the prerequisite (professional account) and error behavior, but lacks details on side effects, permissions, or success return format. The output schema exists but is not described.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the action, and every sentence provides necessary information without fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has one parameter and an output schema, the description adequately covers the purpose, required condition, and error behavior. The rule states that if output schema exists, description need not explain return values, so completeness is high.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With schema description coverage at 0%, the parameter 'handle' has no schema-level description. The tool description adds meaning by stating it must be an Instagram handle for a professional account, which compensates well for the lack of schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action 'Track a new artist' with the method 'by Instagram handle', and it distinguishes from sibling tools like list_artists or remove_artist.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description specifies that the handle must resolve to a professional Business/Creator account, otherwise an error is returned. This provides clear usage conditions, though it does not explicitly list when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_feedA
Return recent posts from all tracked artists, newest first.
Returns metadata and permalinks only (no images) to keep context light.
| Name | Required | Description | Default |
|---|---|---|---|
| limit_per_artist | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description discloses key behaviors: ordering (newest first) and content limitations (no images). No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences with the main purpose front-loaded and no unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
While the output schema exists and the description covers return type, the lack of parameter documentation leaves a gap for a simple tool with one optional parameter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, 'limit_per_artist', is not described in the description. Schema coverage is 0%, so the description should compensate but fails to mention the parameter at all.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it returns recent posts from all tracked artists, newest first, distinguishing it from sibling tools that manage artists or preferences.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description specifies that only metadata and permalinks are returned (no images), guiding the agent on what to expect, though it does not explicitly state when to use it over alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_preference_summaryA
Return every recorded taste preference, so a fresh session can reload it.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, but description only states purpose and use case; lacks disclosure of authentication needs, rate limits, or other behavioral aspects beyond read-only nature.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, efficient, no redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple zero-parameter tool with output schema, the description adequately covers purpose and a use case; could clarify the term 'taste preference' but minor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters in input schema, so baseline is 4; description does not need to add parameter info.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the action ('Return') and resource ('every recorded taste preference'), distinguishing it from sibling tools like record_preference which are writes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Implied usage from 'so a fresh session can reload it' provides context, but no explicit guidance on when not to use or alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_artistsA
List the tattoo artists currently being tracked.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It only states the function and does not disclose any behavioral traits such as read-only nature, pagination, order, or limits. The tool is simple, but transparency is minimal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no wasted words. It is front-loaded with the action and resource.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has zero parameters and an output schema exists, the description is adequate for simple listing. It could mention the return type but the output schema covers that. Slightly above average completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has no parameters, so schema description coverage is 100%. Per guidelines, baseline is 4. Description adds no parameter info but none is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb ('List') and the resource ('tattoo artists currently being tracked'). It distinguishes from sibling tools like add_artist and remove_artist by focusing on retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool versus alternatives (e.g., get_feed or search queries). The description simply states what it does without context on prerequisites or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_inspirationA
List saved inspiration items, in the order they were saved.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. States ordering but lacks details on side effects, pagination, or data scope. Adequate but minimal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence with clear verb and context. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Simple tool with zero parameters and output schema present. Description provides essential function and ordering, sufficient given other schemas.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters, so schema coverage is 100%. Baseline set at 4; description adds no param info as none exist.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the tool lists saved inspiration items in saved order. Differentiates from siblings like 'list_artists' and 'get_feed' by specifying resource and ordering.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool vs alternatives like 'next_inspiration' or 'get_feed'. Simple listing assumed, but explicit context missing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
next_inspirationA
Show one not-yet-seen post for inspiration, then mark it seen.
Returns a ChatGPT Apps SDK widget containing the downscaled preview image,
artist handle, caption, and permalink. The model receives concise text to
narrate; the image is delivered via the widget so it renders in ChatGPT.
Calling repeatedly walks through unseen posts; use reset_seen to start over.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses behavioral traits: it marks the post as seen (mutation), returns a ChatGPT Apps SDK widget with specific content, and that the image is delivered via the widget for rendering. It also explains the sequential walking through unseen posts.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: first defines purpose and side effect, second describes return widget content, third gives usage pattern and sibling reference. No fluff, highly efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no parameters, no output schema, and no annotations, the description is thoroughly complete: it explains the action, return type, side effect, widget rationale for rendering, and references a sibling for resetting. Covers all necessary context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, and schema coverage is 100% vacuously. The description needs no parameter information; it sufficiently explains what the tool does without input, making it clear no parameters are required.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool shows 'one not-yet-seen post for inspiration' and marks it seen. The verb 'show' and resource 'post' with the qualifier 'not-yet-seen' precisely defines the action, distinguishing it from siblings like reset_seen.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Calling repeatedly walks through unseen posts; use reset_seen to start over.' This provides when to use (get next inspiration) and when to use an alternative (reset_seen), giving clear usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
record_preferenceA
Record a note about the user's tattoo taste.
IMPORTANT: Before calling this tool, propose the observation to the user in
your own words and obtain their explicit confirmation. Only call it once the
user has agreed the observation is accurate. This captures taste (e.g.
"prefers fine-line botanical work"), which is distinct from saving a
specific image with save_to_inspiration.
| Name | Required | Description | Default |
|---|---|---|---|
| observation | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | |
| observation | Yes | |
| created_at | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description must disclose behavior. It notes the tool requires user confirmation and captures taste vs images, but does not indicate whether previous preferences are overwritten or appended, or if there are limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact, front-loaded with an important usage warning in bold, and every sentence adds value. No unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple tool with one parameter and an output schema (not shown), the description covers the key context: user confirmation need, distinction from image saving, and parameter example. Could mention return value behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter 'observation' has 0% schema description coverage, so the description adds value by providing an example ('prefers fine-line botanical work') and explaining it captures taste, which goes beyond the plain string type.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool records a note about the user's tattoo taste, with a specific example. It distinguishes from save_to_inspiration, though 'taste' could be more precisely defined.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs the agent to propose the observation to the user and obtain confirmation before calling. Also contrasts with save_to_inspiration, providing clear when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_artistB
Stop tracking the artist with the given handle.
| Name | Required | Description | Default |
|---|---|---|---|
| handle | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description must disclose all behavioral traits. It only says 'Stop tracking' without explaining side effects, permissions, or error conditions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, efficient, front-loaded with verb. Could add a bit more detail without losing conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Minimal but adequate for a simple tool with one parameter and output schema. Lacks context about idempotency or return behavior, but sufficient for basic selection.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the description merely mentions 'the given handle' without elaborating what a handle is or how it should be formatted. Does not compensate for schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the action ('Stop tracking') and resource ('artist with the given handle'), distinguishing it from sibling tools like 'add_artist'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives (e.g., when to remove vs. add or list artists). Agent must infer usage from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_from_inspirationB
Remove a saved inspiration item by post id.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description bears full responsibility for behavioral disclosure. It states 'Remove' but omits details such as permanence, error handling for invalid post_id, permissions required, or side effects. This is insufficient for a deletion operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, concise sentence with no superfluous words. It delivers the core information efficiently.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple removal tool with one parameter, the description is minimally adequate. An output schema exists but is not described. The lack of behavioral details (e.g., confirmation, reversibility) leaves some gaps, but the core functionality is communicated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It only says 'by post id,' adding no meaning beyond the parameter name. It doesn't specify format, constraints, or behavior when post_id is missing or invalid.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Remove'), the resource ('saved inspiration item'), and the identifier ('by post id'). It effectively distinguishes the tool from siblings like 'save_to_inspiration' and 'list_inspiration'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus alternatives. There is no mention of prerequisites, when not to use it, or trade-offs compared to sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reset_seenA
Clear the seen-set so next_inspiration starts fresh.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It clearly states the tool mutates state by clearing a seen-set. For a zero-parameter tool, this is sufficient, though it could mention side effects like affecting other tools.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, concise sentence with no wasted words. It is front-loaded with the action and purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simplicity (0 params, no output schema details needed), the description is adequate. However, it could mention prerequisites or irreversible nature for completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has no parameters, and schema coverage is 100% (not applicable). Baseline for zero parameters is 4, and the description adds no additional parameter information, which is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Clear') and resource ('seen-set') and explicitly states the effect on 'next_inspiration'. It clearly distinguishes this tool from siblings like 'next_inspiration' and 'list_inspiration'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage (to start fresh with 'next_inspiration') but does not explicitly state when not to use it or mention alternatives. Context is clear but lacks exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_to_inspirationC
Bookmark a post (by id, from the current feed) into saved inspiration.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| notes | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| post_id | Yes | |
| artist_handle | Yes | |
| image_url | Yes | |
| permalink | Yes | |
| timestamp | Yes | |
| notes | No | |
| saved_at | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must disclose behavioral traits. It mentions 'bookmark' implying a write operation, but does not state side effects, restrictions (e.g., duplicate handling), or whether notes are saved. The return value is not mentioned.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence, front-loaded with the action. However, it is too brief and omits important details, making it incomplete rather than concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema (as per context signals), the description lacks context on what the tool returns or any failure cases. For a simple tool with two parameters, more completeness is expected.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, yet the description adds no meaning for the parameters. 'post_id' and 'notes' are not explained beyond their names. The purpose of 'notes' and how 'post_id' relates to the feed are unclear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Bookmark'), the resource ('a post by id, from the current feed'), and the destination ('into saved inspiration'). It distinguishes the tool from related siblings like 'remove_from_inspiration' and 'list_inspiration'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the post must be from the current feed, but does not explicitly state when to use this tool versus alternatives like 'add_artist' or 'reset_seen'. No 'when not to use' guidance is provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
11 tool updates
v0.1.0- First observed
add_artist - First observed
get_feed - First observed
get_preference_summary - First observed
list_artists - First observed
list_inspiration - First observed
next_inspiration - First observed
record_preference - First observed
remove_artist - First observed
remove_from_inspiration - First observed
reset_seen - First observed
save_to_inspiration
TDQS
Scored across 11 tools
Each tool targets a clearly distinct responsibility: artist management, feed viewing, inspiration, and preferences. No overlapping purposes.
All tool names follow a consistent verb_noun pattern in snake_case (e.g., add_artist, get_feed, record_preference), making the surface predictable.
11 tools is well-scoped for the domain of tracking tattoo artists, browsing feeds, and managing inspiration and preferences.
Core workflows are covered: CRUD for artists and inspiration, preference recording, and feed retrieval. Minor gaps include no update/delete for preferences and no filtered feed per artist, but these are not critical.
Maintenance
Related MCP Connectors
Instagram data for AI agents: profiles, posts, reels, followers. Influencer + brand research.
Instagram for AI agents: publish, read comments and DMs, insights, and engage from your account.
Instagram profiles for AI agents — followers, similar accounts, keyword and location search.
Create, review, publish and schedule Instagram images, carousels and Reels with AI assistants.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables LLMs to interact with Instagram through a comprehensive toolkit for account management, content creation, messaging, social graph analysis, and content discovery.10-
- AlicenseNot gradedqualityDmaintenanceEnables AI applications to interact with Instagram Business accounts through the Graph API, supporting profile management, media publishing, insights retrieval, and direct messaging capabilities.MIT
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage Instagram Business accounts by automating content publishing, scheduling posts, and analyzing performance metrics. Supports posts, stories, reels, and carousels with detailed audience insights and hashtag discovery.-
- AlicenseAqualityFmaintenanceEnables AI assistants to interact with Instagram by scraping profiles, posts, reels, DMs, and business insights through a robust, DOM-agnostic browser orchestration engine that bypasses Instagram's anti-automation measures.281Apache 2.0