Bringing HubSpot Brand Context into AI Tools

SUMMER 2026 · 10 MIN READ

Role
Software Engineer InternatHubSpot
Overview
I spent this summer at HubSpot working on MCP tools that bring brand context into AI assistants and exploring problems around tool invocation, interactive review, and combining model reasoning with deterministic code.

During my internship at HubSpot, I worked on the Brand Identity team across several backend and product initiatives, including a problem that became more interesting as I moved through the stack: an AI model can write a convincing product announcement while knowing very little about the company it is writing for. Its choices about language, emphasis, and audience depend on the context available when it starts writing.

A company might prefer direct language, describe its products in a particular way, or adjust its tone for different audiences. HubSpot's public Brand Identity features capture context such as voice, tone, ideal customer profile, products, and competitive information for use in AI-generated content. That gives writing tools a shared reference for decisions that would otherwise depend on whatever the user happens to include in a prompt.

The writing process also extends beyond the application where that context lives. A person might draft an email in an AI assistant and return to HubSpot later to prepare a campaign. HubSpot's remote Model Context Protocol server provides a public foundation for connecting compatible AI clients to HubSpot data, with OAuth authentication and access governed by the user's permissions.

My work explored the Brand Identity side of that connection, from helping a model retrieve relevant context to building an interactive content-review prototype. The account below describes those engineering decisions using generalized tool names and illustrative data; the review prototype and its example interfaces describe the work I explored during the internship rather than a catalog of publicly available HubSpot features.

Getting brand context into the conversation

Model Context Protocol, or MCP, gives an AI client a standard interface for discovering tools and invoking them. In a writing workflow, a tool can retrieve brand guidance before the model starts drafting, allowing that guidance to participate in the same conversation as the user's request.

The interaction begins with an ordinary prompt, such as “Write a launch email for our new analytics feature.” The client exposes the available tool definitions to the model, the model selects a context-retrieval tool, and the result supplies information that can guide the response. The conversation remains in the AI client while the connected service supplies the requested context.

Fig. 1Illustrative context
Brand context across the MCP boundaryInside the AI client, a writing prompt enters the conversation, the model selects a context tool from the tool registry, and a tools/call request crosses the MCP boundary to a context tool in the connected HubSpot service. The tool reads brand context covering voice and tone, audience, products, and messaging. The tool result returns to the conversation, and the draft begins after the context arrives.AI CLIENTHUBSPOTConceptual Brand Identity integrationMCPmodel selects a tooltools/calltool resultthenConversation“Write a launch email for ournew analytics feature.”Tool registryDefinitions exposed to the modelDraftWaits forcontextContext toolRetrieves brand guidanceBrand contextVoice & toneAudienceProductsMessagingBrand context across the MCP boundaryInside the AI client, a writing prompt enters the conversation, the model selects a context tool from the tool registry, and a tools/call request crosses the MCP boundary to a context tool in the connected HubSpot service. The tool reads brand context covering voice and tone, audience, products, and messaging. The tool result returns to the conversation, and the draft begins after the context arrives.AI CLIENTHUBSPOTConceptual Brand Identity integrationMCPtools/calltool resultConversation“Write a launch email for ournew analytics feature.”Tool registryDefinitionsexposed to modelDraftWaits forcontextContext toolRetrieves brand guidanceBrand contextVoice & toneAudienceProductsMessaging
Steps

Press play, or choose a step or a node in the diagram, to follow the request.

Payload inspector
{
  "method": "tools/call",
  "params": {
    "name": "get_brand_context_before_writing"
  }
}

Synthetic payload. Keys are illustrative and are not an API schema.

Figure 1. Brand context across the MCP boundary. A writing assistant retrieves brand guidance through an MCP tool and uses the returned context when generating a draft. Tool names and payload keys are illustrative, not an internal or public schema, and the boundaries are a conceptual overview rather than a deployment topology.

The first difficulty appeared at the selection step. A tool could be present in the client's registry and still be overlooked when the model decided how to answer. Retrieving context required the model to recognize that an apparently ordinary writing task depended on information held elsewhere.

That made the tool interface part of the writing workflow itself. Its name and description needed to connect the user's intent to the capability the tool provided.

Designing an API for a model

A traditional API usually has a developer between its documentation and the decision to call it. The developer understands the application, reads the endpoint contract, and chooses where the call belongs. With a model selecting tools, that reasoning takes place against the definitions supplied in the current context.

In the behavior I observed, requests that explicitly mentioned brand voice or brand identity gave the model a clear reason to retrieve the relevant guidance. A request to write a launch email gave it a weaker signal, even though brand context was useful in both cases.

I first made the tool description more explicit about when the context should be loaded. It connected retrieval to content-generation tasks and explained why brand guidance mattered even when the user had omitted the phrase “brand voice.” Supporting instructions reinforced that relationship across the writing workflow.

The next iteration moved that intent into the tool name. A data-oriented label such as retrieve_brand_identity describes the information returned by a call. A workflow-oriented label such as get_brand_context_before_writing also gives the model a cue about when to use it. These are generalized examples of the distinction I was working toward.

Fig. 2Scripted illustration
User prompt · identical in every state

“Write a launch email for our new analytics feature.”

Selection path for the launch-email promptThe prompt either goes directly to generation, or calls the context tool, receives a context result, and then generates. In state 1 the direct path is shown; in states 2 and 3 the path goes through context retrieval.direct to generationLaunch-email promptSame in every stateContext tool calltools/callContext resultBrand guidanceGenerationDraftSelection path for the launch-email promptThe prompt either goes directly to generation, or calls the context tool, receives a context result, and then generates. In state 1 the direct path is shown; in states 2 and 3 the path goes through context retrieval.directLaunch-email promptSame in every stateContext tool calltools/callContext resultBrand guidanceGenerationDraft

Choose a state, or press play, to compare the three definitions.

Tool definition
name
retrieve_brand_identity
description
Returns the company's brand identity.
One of several context tools listed in discovery.
Example draft

Introducing our revolutionary analytics platform. It transforms reporting for everyone.

Inspect a context field

 

Figure 2. Inspecting the tool-selection trace. Three tool definitions make the link between writing and brand-context retrieval progressively more explicit. This is a scripted illustration of the design rationale, not a live model run, benchmark, or guarantee of selection behavior. Drafts are synthetic.

I also simplified the externally visible tool surface. Overlapping ways to retrieve similar guidance created another choice for the model, so I reduced that overlap while keeping existing callers supported. When the clearer interface replaced the previous name in discovery, the previous entry point remained callable for compatibility.

The distinction between discoverability and compatibility was useful here. New clients could see the interface we wanted them to use, while existing integrations retained a working path. Together, the naming, description, and tool-surface changes made the relationship between a writing request and its required context more explicit.

This work changed how I approached model-facing APIs. A tool definition provides a contract for execution and a cue for selection, so its wording deserves the same attention as the implementation behind it.

Generation was only one side of the experience

Once brand context could participate in drafting, I started thinking about what a user needed when reviewing an existing document. A rewritten paragraph provides a candidate result, but the review also needs to explain how that result relates to the original.

A user may want to inspect the previous wording, understand the brand guidance behind a revision, reject a particular change, or ask for another pass on one sentence. Those decisions become harder to manage when the revised document and every explanation occupy a single stream of assistant text.

I began building an interactive Brand Voice Review prototype around that workflow. The conversation initiated the review, while an embedded interface represented the document and its changes. Selecting a revision could reveal the original wording and its explanation, with feedback actions attached to the change being inspected.

That interface required the backend to return a structured relationship between the original document, the revised document, and the explanations. A revised string alone left too much work for the display layer to reconstruct.

Separating the rewrite from the diff

One possible implementation would ask a model to perform the entire review in one call: rewrite the document, identify every edit, classify the edits, preserve formatting, explain each change, produce a summary and score, and serialize the result for the frontend.

Those tasks have different requirements. Rewriting according to brand guidance involves interpretation, while comparing two known strings requires exact accounting of their contents. Asking the model to handle both makes the document contract depend on the same probabilistic process that produces the language choices.

I separated the review into three stages. The first model call rewrote the content using brand guidance. Application code compared the original and revised strings and produced an ordered set of segments. A second model call received the changed sections and the relevant guidance to generate explanations, an overall summary, and an alignment score.

Fig. 3Illustrative
Rewrite, diff, and explanation pipelineInside the connected review service, brand context and the original document enter a rewrite model. The original and revised strings enter a deterministic diff engine. Changed segments and brand guidance enter an explanation model. Segments go directly to response assembly, and explanations join as optional enrichment. The structured review reaches the review UI in the AI client.CONNECTED REVIEW SERVICEAI CLIENToriginalbrand guidancesegmentsoptionalBrand contextguidanceOriginaldocumentRewritemodel call 1Diff enginedeterministicExplanationsmodel call 2ResponseassemblyReview UIembeddedRewrite, diff, and explanation pipelineInside the connected review service, brand context and the original document enter a rewrite model. The original and revised strings enter a deterministic diff engine. Changed segments and brand guidance enter an explanation model. Segments go directly to response assembly, and explanations join as optional enrichment. The structured review reaches the review UI in the AI client.CONNECTED REVIEW SERVICEAI CLIENToriginalsegmentsoptionalBrand contextguidanceOriginaldocumentRewritemodel call 1Diff enginedeterministicExplanationsmodel call 2ResponseassemblyReview UIembedded
  • Model call
  • Deterministic code
  • User-facing
  • Optional enrichment
Stages
Inspector · RewriteSelect a node to inspect it

Inputs

guidance: Prefer concrete product language. Avoid exaggerated claims.
original: Our revolutionary analytics platform transforms reporting.

Candidate revision

Our analytics workspace helps teams understand their data.

Figure 3. An inspectable rewrite, diff, and explanation pipeline. A model rewrites the document, deterministic code computes the exact changes, and a second model adds optional explanations before the review is rendered. Boxes group conceptual responsibilities, not deployment boundaries. Payloads are generalized and illustrative.

This division gave the model responsibility for language decisions and gave application code responsibility for the exact relationship between two documents. It also made the inputs to explanation generation more focused: the second call could reason about known changes instead of discovering them again.

The alignment score belonged to the same interpretive layer as the explanations. It summarized a model's assessment of the draft against the supplied guidance, so the interface needed to present it as a review aid rather than a measured probability of correctness.

Giving the frontend a diff it could trust

The review represented the document with four segment types. Unchanged segments carried text through directly, added segments introduced new text, removed segments retained text that had been deleted, and modified segments paired original wording with its replacement.

The contract behind those categories was more important than the labels: rendering the segments in order had to reconstruct the revised content exactly, including whitespace and line breaks. That gave the frontend a reliable document representation while retaining enough information for inspection.

The following simplified example shows the relationship. The field names illustrate the contract rather than reproducing an implementation schema.

[
  { "type": "UNCHANGED", "text": "Hello everyone.\n\n" },
  {
    "type": "MODIFIED",
    "original": "Our revolutionary platform",
    "revised": "Our analytics workspace"
  },
  { "type": "UNCHANGED", "text": " helps teams.\n" }
]

The unchanged values and the modified segment's revised value combine into the exact revised document. Added text contributes its new value, while removed text contributes an empty string to that reconstruction. Removed text remains available separately for the review interface.

renderRevised(segments) === revisedContent
Fig. 4Illustrative
Fixture
Original
Hello everyone.

Our revolutionary platform helps teams.
Segments
  • 0 UNCHANGED
  • 1 MODIFIED
    old
    "Our revolutionary platform"
    new
    "Our analytics workspace"
    orig
    [17, 43)
    rev
    [17, 40)
  • 2 UNCHANGED

Arrow keys move between segments.

Reconstructed revision
Press Reconstruct to combine the revised values in order.

Figure 4. The segment debugger. Ordered segments reconstruct the revised string exactly, including its blank line and trailing newline. Whitespace markers are display-only and excluded from copied text.

Real documents contain headings, blank paragraphs, leading or trailing whitespace, and different line-ending conventions. A change to one sentence can sit beside any of those structures, and a segment renderer must preserve their relationship to the text around them. Consecutive edits and whitespace-only sections make that requirement especially visible.

I added tests for reconstruction across multi-paragraph content, leading and trailing whitespace, Windows line endings, whitespace-only input, and combinations of segment types. Those checks placed the exactness requirement in the backend contract, giving the frontend a representation it could render directly.

The diff established what changed, while the explanation stage interpreted why the change might fit the brand. Keeping those responsibilities separate made both easier to inspect.

Treating explanations as enrichment

The staged pipeline also exposed a distinction between the result required for a review and the information that enriches it. The rewrite provides the candidate document, and the diff provides a trustworthy representation of its changes. Explanations add useful interpretation after those two pieces exist.

If explanation generation failed after the rewrite and diff succeeded, the system could still return the revised content and structured segments. The summary, explanations, and score could remain absent from that response, preserving the completed work for the user to inspect.

Fig. 5Illustrative
Required stages and optional enrichmentRewrite leads to a deterministic diff, which leads directly to the review result. An optional branch sends the diff and brand context to explanations, which enrich the review result. If rewrite or diff fails, the review could not complete. If explanations fail, the review still has revised text and segments.direct+ brand contextRewriterequiredtransformationDeterministic diffrequired contractExplanationsoptionalenrichmentReview resulttext + segments+ explanationsReview could not completerequired stage failedRequired stages and optional enrichmentRewrite leads to a deterministic diff, which leads directly to the review result. An optional branch sends the diff and brand context to explanations, which enrich the review result. If rewrite or diff fails, the review could not complete. If explanations fail, the review still has revised text and segments.direct+ brand contextRewriterequiredtransformationDeterministic diffrequired contractExplanationsoptionalenrichmentReview resulttext + segments+ explanationsReview could not completerequired stage failed

Full review. Every stage succeeds. The review carries revised text, segments, explanations, and a score.

Figure 5. Required stages and optional enrichment. Successful rewriting and diffing still produce a usable review when optional explanation generation fails, while a failure in a required stage prevents the review from completing. Dashed edges mark optional enrichment.

This behavior followed the dependency structure of the workflow. An explanation failure affected the interpretation layer, while a rewrite failure left the review without a revised document. The response could therefore reflect the value that had actually been produced, and the interface could make missing enrichment visible without discarding a valid diff.

Turning an MCP response into an application

Once the backend returned structured segments, the frontend could represent the review as an editable document with inspectable changes. Unchanged text remained visually quiet, modifications became selectable, and removed text could be inspected separately from the revised version.

I built the review interface in React. Selecting a modification opened a panel with the original wording and the associated explanation, while actions such as rejecting a change or requesting another revision operated on the selected part of the document. Copying the revised text needed to produce a clean document that excluded removed content and review annotations.

Fig. 6Illustrative prototype · simulated review
Draft

Introducing our revolutionary analytics platform

Teams can now build reports from the data they already collect. It transforms reporting overnight and guarantees better decisions. It is the most powerful tool on the market.

Leverage synergies across every dashboard and share reports with the people who need them.

Read the setup guide to get started.

Review trace
  1. Context loadedpending
  2. Rewrite completepending
  3. Segments generatedpending
  4. Explanations attachedpending
  5. Response renderedpending

Run the review to inspect each change.

Summary

Appears after the review.

Model-estimated alignment

—

Figure 6. Interactive Brand Voice Review. A simulated review: select a highlighted change to see the original wording and the guideline behind it, then reject it or request a targeted revision. Everything runs on a predefined fixture; there are no live HubSpot or model calls, and the score is an illustrative model estimate rather than measured accuracy.

Rendering real documents revealed details that were easy to overlook in a single-paragraph example. Markdown headings needed to remain block elements, paragraph spacing had to survive segmentation, and a multiline change needed to stay associated with the correct selection target. The original and revised documents could also have different paragraph structures, so matching them by paragraph index produced fragile assumptions.

I added normalization in the React layer to handle those variations while keeping the underlying document contract intact. The interface could organize the review for display and still derive clean copied text from the actual revised values.

At that point, the tool response had become application state. The conversation provided a natural place to ask for a review, and the embedded interface provided controls suited to the decisions the user needed to make afterward.

Revising one change while preserving the others

Interactive review introduces a continuity problem as soon as a user starts approving individual changes. If a review contains eight revisions and seven have been accepted, the next request should focus on the remaining revision while carrying those earlier decisions forward.

Running the original rewrite over the entire document gives the model scope to reconsider all eight changes. From the user's perspective, that broadens a local request into another document-wide review and makes their previous decisions harder to preserve.

I added a targeted feedback flow that carried the requested change alongside the content to preserve. The backend reused the review path while constraining the next pass around the part of the document that still needed work. Accepted content became part of the next request's constraints, giving the workflow a clearer relationship to the user's accumulated decisions.

This made the interaction closer to document editing, where each pass builds on decisions already made. It also clarified the state the application needed to carry: the current revision, the selected region, the user's feedback, and the material that should remain preserved.

Tool discovery has its own semantics

Another integration issue appeared before the model invoked a writing tool. Discovery tells a client which capabilities are available, while execution attempts to use one of those capabilities. A permission condition can have a different meaning in each operation.

In the path I investigated, a capability-specific permission check could turn discovery into an authorization error. For an otherwise valid connection whose user lacked access to that capability, the useful result was a successful discovery response with an empty available-tool set.

I traced that behavior through the configuration path and adjusted the handling so discovery could express capability availability, while tool execution continued to enforce authorization. The client could then distinguish a valid connection with no relevant tools from a connection or request that had actually failed.

Fig. 7Illustrative

Precondition: valid authenticated connection; capability unavailable to this user.

Discovery and execution as a protocol traceDiscovery: the client sends tools/list to capability configuration. Discovery succeeds with an empty tool list: the connection is valid and has no relevant tools. Execution: the client sends tools/call to tool execution, which checks authorization and returns an allowed result or a denied result.ClientCapability configurationTool executionDISCOVERYEXECUTIONtools/list{ "tools": [] }returned successfullyavailability checktools/callallowed result or deniedauthorization enforcedexecution authorizationDiscovery and execution as a protocol traceDiscovery: the client sends tools/list to capability configuration. Discovery succeeds with an empty tool list: the connection is valid and has no relevant tools. Execution: the client sends tools/call to tool execution, which checks authorization and returns an allowed result or a denied result.ClientCapability configExecutionDISCOVERYEXECUTIONtools/list{ "tools": [] }returned successfullyavailabilitychecktools/callallowed result or deniedauthorization enforcedexecutionauthorization

After. Discovery succeeds with an empty tool list: the connection is valid and has no relevant tools. An empty list is not permission to execute an unavailable tool, and genuine authentication or request failures remain failures.

Figure 7. Discovery and execution as a protocol trace. A valid connection can successfully discover zero available tools, and executing a tool still requires authorization. The diagram shows a capability-specific availability decision, not a rule that every discovery error becomes an empty list. Method names are representative MCP operations; boundaries and payloads are illustrative.

That distinction gave the client more useful information for its next decision. An empty tool list describes the capabilities of the current connection, while an error describes a failure that may require recovery. Both outcomes belong in the protocol, and each should retain its meaning.

The debugging work reinforced a pattern that ran through the project: the experience depends on the behavior surrounding the model call. Tool discovery, document contracts, partial failures, and feedback state all shape what a user can reliably do with the generated result.

Deciding what belongs in the model

The project brought together several forms of reasoning with different requirements. Brand-aware rewriting involved interpretation, tool selection depended on a clear description of intent, and explanations connected language choices back to guidance. Exact document reconstruction belonged in deterministic code, while review decisions belonged with the user.

Making those responsibilities explicit gave the workflow a structure I could test and inspect. The model produced candidate language, the diff established its relationship to the original, and the interface let the user decide which changes to keep. Optional explanations could improve the review while the underlying document remained useful on its own.

I came away with a more concrete way to think about building around language models: define the contracts that the surrounding software can enforce, give the model the context needed for its interpretive work, and preserve the user's decisions as the workflow continues. For this project, that meant carrying brand guidance into the conversation and carrying a trustworthy document through every subsequent stage of review.